fonctionnalites:geonature:synthese-amelioration-performance-test

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Les deux révisions précédentes Révision précédente
Prochaine révision
Révision précédente
fonctionnalites:geonature:synthese-amelioration-performance-test [2024/06/25 10:40] – [Annexe 18 - Test utilisation table "cor_area_synthese" sans lien vers la "synthese"] jpmilcentfonctionnalites:geonature:synthese-amelioration-performance-test [2024/08/05 08:07] (Version actuelle) okristo
Ligne 1: Ligne 1:
-====== Synthese - Tests d'amélioration des performances ======+====== Synthèse - Tests d'amélioration des performances ======
  
 ===== Objectif ===== ===== Objectif =====
Ligne 7: Ligne 7:
 ===== Principe général ===== ===== Principe général =====
  
-Afin d'améliorer les performances du module Synthèse de GeoNature, nous pouvons remplacer le web service geojson actuel servant à la fois au rendu sur la carte et à l'affichage des données en liste par plusieurs web services spécialisés. +Afin d'améliorer les performances du module Synthèse de GeoNature, nous pouvons remplacer le web service geojson actuelservant à la fois au rendu sur la carte et à l'affichage des données en listepar plusieurs web services spécialisés. 
-Ces web services devront retourner des données relativement constantes quelque soit le nombre d'observation à afficher résultant de la recherche effectuée dans la Synthese.+Ces web services devront retourner des données relativement constantes quel que soit le nombre d'observations à afficherrésultant de la recherche effectuée dans la Synthèse.
  
-Nous pouvons distinguer 3 principaux type de web services dans le module Synthese qui fournissent des données à +Nous pouvons distinguer 3 principaux types de web services dans le module Synthèse qui fournissent des données : 
-  * le tableau d'informations paginée  +  * au tableau d'informations paginée  
-  * l'export des données +  * à l'export des données 
-  * la carte+  * à la carte
 Nous proposerons des solutions pour chacun d'entre eux. Nous proposerons des solutions pour chacun d'entre eux.
  
 ==== Solutions pour le tableau d'information paginée ==== ==== Solutions pour le tableau d'information paginée ====
-Dans le cas des observations présentées sous forme de tableau paginées, la solution la plus adéquates consiste à **créer un web service paginée côté serveur**. L'avantage de ce type de web service c'est que le nombre d'informations retournées est constant quelque soit les résultats de la recherche. Son utilisation dans certains modules de GeoNature a montré son efficacité et sa facilité d'utilisation avec le composant Datatable d'Angular Material.+Dans le cas des observations présentées sous forme de tableau paginé, la solution la plus adéquate consiste à **créer un web service paginé côté serveur**. L'avantage de ce type de web service est que le nombre d'informations retournées est constant quels que soient les résultats de la recherche. Son utilisation dans certains modules de GeoNature a montré son efficacité et sa facilité d'utilisation avec le composant Datatable d'Angular Material.
  
-Côté serveur, il sera nécessaire de s'assurer que les requêtes exécuter en base de données soient toujours les plus performantes possibles. Si nous souhaitons garder un tri similaire des observations, il faudra rassembler les observations sélectionnée via la carte en début de liste.+Côté serveur, il sera nécessaire de s'assurer que les requêtes exécutées en base de données soient toujours les plus performantes possibles. Si nous souhaitons garder un tri similaire des observations, il faudra rassembler les observations sélectionnées via la carte en début de liste.
  
 Côté navigateur client, ce mécanisme permet de s'assurer que les performances seront toujours les mêmes. Il faudra toutefois peut être retravailler le mécanisme d'interaction entre les observations de la carte et du tableau. Il semble toutefois possible de s'appuyer sur la valeur du champ ''id_synthese'' pour le maintenir. Côté navigateur client, ce mécanisme permet de s'assurer que les performances seront toujours les mêmes. Il faudra toutefois peut être retravailler le mécanisme d'interaction entre les observations de la carte et du tableau. Il semble toutefois possible de s'appuyer sur la valeur du champ ''id_synthese'' pour le maintenir.
  
 ==== Solutions pour l'export des données ==== ==== Solutions pour l'export des données ====
-Le travail effectué en 2023 sur le module Export a démontré qu'il était possible d'exporter un grand volume de données sans surcharger la mémoire et l'espace disque du serveur. Mais pour cela, il est nécessaire de générer l'export en arrière plan et alerter l'utilisateur lorsque l'export est prêt à être téléchargé.+Le **travail effectué en 2023 sur le module Export** a démontré qu'il était possible d'exporter un grand volume de données sans surcharger la mémoire et l'espace disque du serveur. Mais pour cela, il est nécessaire de générer l'export en arrière plan et alerter l'utilisateur lorsque l'export est prêt à être téléchargé.
  
-Pour cela, les nouveaux mécanismes tels que l'utilisation de tâches Celery et les notifications ajoutés dans les dernières version de GeoNature nous permettrons de mettre en place un export performant avec des limites très largement augmentées.+Pour cela, les nouveaux mécanismes tels que l'**utilisation de tâches Celery** et les **notifications** ajoutés dans les dernières version de GeoNature nous permettrons de mettre en place un export performant avec des limites très largement augmentées.
  
 Toutefois, la mise en place de cette solution nécessitera de revenir sur la branche principale de GeoNature dans laquelle les tâches Celery et les notifications ont été implémentées. Toutefois, la mise en place de cette solution nécessitera de revenir sur la branche principale de GeoNature dans laquelle les tâches Celery et les notifications ont été implémentées.
  
 ==== Solutions pour la carte ==== ==== Solutions pour la carte ====
-L'amélioration des performances d'affichage sur la carte et le point qui nécessite le plus d'expérimentation car aucune solution satisfaisante n'a encore été expérimenté dans GeoNature. Nous réaliserons donc la majorité des nos tests sur ce sujet particulier.+L'amélioration des performances d'affichage sur la carte et le point qui nécessite le plus d'expérimentation car aucune solution satisfaisante n'a encore été expérimentée dans GeoNature. Nous réaliserons donc la majorité des nos tests sur ce sujet particulier.
  
-Toutefois, le web service GeoJson actuel a vu ces performances largement améliorées lors de travaux effectués en 2022 avec le regroupement des observations par géométries identiques. Une optimisation de la création du GeoJson a également été effectuée lors des travaux visant à permettre le regroupement et l'affichage des observations par mailles sur la carte de la Synthese.+Toutefois, le web service GeoJson actuel a vu ses performances largement améliorées lors de travaux effectués en 2022 avec le regroupement des observations par géométries identiques. Une optimisation de la création du GeoJson a également été effectuée lors des travaux visant à permettre le regroupement et l'affichage des observations par mailles sur la carte de la Synthese.
    
 Afin de poursuivre l'amélioration de l'affichage d'une grand nombre d'observations sur la carte, nous envisageons donc d'expérimenter les éléments suivant : Afin de poursuivre l'amélioration de l'affichage d'une grand nombre d'observations sur la carte, nous envisageons donc d'expérimenter les éléments suivant :
-  - Tester la différence entre l'utilisation de la geom avec un SRID 2154 et 4326 pour les intersections+  - Utiliser des tuiles vecteurs ou geojson permettant de paralléliser les requêtes en base de données 
 +  - Mettre en place un web service spécifique à la carto geojson/mvt filtrée sur la bbox de la carte actuellement visualisée 
 +  - Utiliser un affichage différent en fonction du zoom : petit zoom avec mailles, moyen avec cluster/polygones, grand avec points précis. 
 +  - Tester la différence entre l'utilisation de la géométrie avec un SRID 2154 et 4326 pour les intersections
   - Comparer la rapidité des intersections de géométrie à l'aide de : ''st_intersects()'' ou de ''&&''   - Comparer la rapidité des intersections de géométrie à l'aide de : ''st_intersects()'' ou de ''&&''
-  - Comparer différents types d'index (GIST, BRIN, SP-GIST) sur le champ geométrie de la table Synthese+  - Comparer différents types d'index (GIST, BRIN, SP-GIST) sur le champ géométrie de la table Synthèse
   - Comparer l'utilisation d'une intersection spatiale vis à vis de l'utilisation d'une table relationnel (''cor_area_synthese'')   - Comparer l'utilisation d'une intersection spatiale vis à vis de l'utilisation d'une table relationnel (''cor_area_synthese'')
-  - Utilisation de tuiles vecteurs ou geojson permettant de paralléliser les requêtes en base de données 
-  - Web service spécifique carto geojson/mvt filtrer sur la bbox de la carte actuellement visualisé 
-  - Utiliser un affichage différent en fonction du zoom : petit zoom avec mailles, moyen avec cluster/polygones, grand avec points précis. 
   - Tenter d'améliorer les performances de ''cor_area_synthese'' :   - Tenter d'améliorer les performances de ''cor_area_synthese'' :
-    - Ajouter une colonne ''area_type_code'' contenant le code du type de zone géo correspondant à l'''id_area''.+    - Ajouter une colonne ''area_type_code'' contenant le code du type de zone géographique correspondant à l'''id_area''.
     - Mettre en place [[https://docs.postgresql.fr/15/ddl-partitioning.html| un partitionnement de la table]] basé sur ''area_type_code''     - Mettre en place [[https://docs.postgresql.fr/15/ddl-partitioning.html| un partitionnement de la table]] basé sur ''area_type_code''
-    - Créer une vue matérialisée mettant en cache les données agrégées par maille pour l'affichage par défaut (nombre d'observation par maille sans filtre) +    - Créer une vue matérialisée mettant en cache les données agrégées par maille pour l'affichage par défaut (nombre d'observations par maille sans filtre) 
-  - Tester l'utilisation d'une table des géométries de ''l_areas'' subdivisées (''st_subdivide'') pour essayer l'agrégation par communes+  - Tester l'utilisation d'une table des géométries de ''l_areas'' subdivisées (''st_subdivide()'') pour l'utiliser lors des intersections (''st_intersects()'')
-  - Tester l'utilisation d'une vue matérialisée correctement indexé et agrégeant l'ensemble des informations nécessaires aux requêtes de la Synthese+  - Tester l'utilisation d'une vue matérialisée correctement indexée et agrégeant l'ensemble des informations nécessaires aux requêtes de la Synthèse
  
 ===== Tests d'amélioration de l'affichage sur la carte ===== ===== Tests d'amélioration de l'affichage sur la carte =====
Ligne 67: Ligne 67:
 Il existe deux format de tuiles vecteurs envisageable : Mapbox Vector Tile (MVT) ou GeoJson. Mais la quasi totalité des exemples et articles sur le web concernent les tuiles MVT. Les tuiles geojson sont-elles moins intéressantes ? Il existe deux format de tuiles vecteurs envisageable : Mapbox Vector Tile (MVT) ou GeoJson. Mais la quasi totalité des exemples et articles sur le web concernent les tuiles MVT. Les tuiles geojson sont-elles moins intéressantes ?
  
-Pour les petits niveau de zoom, nous utiliserons [[https://www.esri.com/arcgis-blog/products/data-management/mapping/strategies-to-effectively-display-large-amounts-of-data-in-web-apps/|une technique d’agrégation permettant d'afficher une grande quantité d'information]]. Nous essaierons de retourner des données agrégées par maille avec une coloration des mailles en fonction du nombre d'observation contenues. Cela permettra de garantir la lisibilité des données affichés.+Pour les petits niveaux de zoom, nous utiliserons [[https://www.esri.com/arcgis-blog/products/data-management/mapping/strategies-to-effectively-display-large-amounts-of-data-in-web-apps/|une technique d’agrégation permettant d'afficher une grande quantité d'information]]. Nous essaierons de retourner des données agrégées par maille avec une coloration des mailles en fonction du nombre d'observations contenues. Cela permettra de garantir la lisibilité des données affichées.
  
-L'utilisation de maille est un facteur important car c'est un objet géographique simple qui ne contient que 5 points. Nous disposons également de mailles de différentes tailles 1, 5 et 10 km par défaut dans GeoNature, ce qui permet de sélectionner la taille la mieux adaptée à son territoire et à la quantité de données hébergée. Enfin, l'intersection de données avec des mailles en base de données peut se faire à l'aide de la fonction Postgis ''ST_Intersects()'' mais également à l'aide de l'opérateur bien plus performant ''&&'' qui pour les géométries signifie "l'étendue recouvre ou touche".+L'utilisation de mailles est un facteur important car c'est un objet géographique simple qui ne contient que 5 points. Nous disposons également de mailles de différentes tailles 1, 5 et 10 km par défaut dans GeoNature, ce qui permet de sélectionner la taille la mieux adaptée à son territoire et à la quantité de données hébergée. Enfin, l'intersection de données avec des mailles en base de données peut se faire à l'aide de la fonction Postgis ''ST_Intersects()'' mais également à l'aide de l'opérateur bien plus performant ''&&'' quipour les géométriessignifie "l'étendue recouvre ou touche".
  
-Enfin, afin de réduire le volume de données à renvoyer par tuile, il est possible de simplifier la géométrie des objets renvoyés. Dans le cadre du module Synthèse de GeoNature, cette possibilité est envisageable mais n'aura pas forcément beaucoup d'intérêt. En effet, les mailles agrégeant les données à petite échelle ne peuvent pas être plus simplifiées. Cette technique peut éventuellement être utilisé pour simplifier les géométries d'observation de type polygone renvoyées à grande échelle. Mais dans ce cas là, nous cherchons souvent à garder le maximum de précision.\\ +Enfin, afin de réduire le volume de données à renvoyer par tuile, il est possible de simplifier la géométrie des objets renvoyés. Dans le cadre du module Synthèse de GeoNature, cette possibilité est envisageable mais n'aura pas forcément beaucoup d'intérêt. En effet, les mailles agrégeant les données à petite échelle ne peuvent pas être plus simplifiées. Cette technique peut éventuellement être utilisée pour simplifier les géométries d'observations de type polygone renvoyées à grande échelle. Mais dans ce cas-là, nous cherchons souvent à garder le maximum de précision.\\ 
-Par contre, il peut être intéressant de simplifier le nombre de décimale des coordonnées des géométries des observation. Avec 5 chiffres après la virgule, une coordonnées est précise au mètre. Il semble donc intéressant de garder 5 (ou 6) chiffres maximum après la virgule.+Par contre, il peut être intéressant de simplifier le nombre de décimales des coordonnées des géométries des observations. Avec 5 chiffres après la virgule, une coordonnée est précise au mètre. Il semble donc intéressant de garder 5 (ou 6) chiffres maximum après la virgule.
  
 La parallélisation de l'accès aux données, leur vectorisation et leur agrégation par mailles devrait rendre possible l'affichage d'une grande quantité de données à petite échelle. La parallélisation de l'accès aux données, leur vectorisation et leur agrégation par mailles devrait rendre possible l'affichage d'une grande quantité de données à petite échelle.
Ligne 81: Ligne 81:
   * bon support du GeoJson par Leaflet   * bon support du GeoJson par Leaflet
   * existence d'un plugin [[https://github.com/glenrobertson/leaflet-tilelayer-geojson| Leaflet Tilelayer GeoJson]]   * existence d'un plugin [[https://github.com/glenrobertson/leaflet-tilelayer-geojson| Leaflet Tilelayer GeoJson]]
-  * le module Synthese de GeoNature exploite déjà ce format+  * le module Synthèse de GeoNature exploite déjà ce format
  
 Cependant, les défauts des tuiles GeoJson comparés aux tuiles MVT sont : Cependant, les défauts des tuiles GeoJson comparés aux tuiles MVT sont :
Ligne 88: Ligne 88:
   * le support des tuiles GeoJson n'est pas offert nativement par les principaux framework carto web : [[https://github.com/mapbox/mapbox-gl-js/issues/7912| Maplibre]] (pas de support), LeafLet (via le plugin Leaflet Tilelayer GeoJson), OpenLayers (?).    * le support des tuiles GeoJson n'est pas offert nativement par les principaux framework carto web : [[https://github.com/mapbox/mapbox-gl-js/issues/7912| Maplibre]] (pas de support), LeafLet (via le plugin Leaflet Tilelayer GeoJson), OpenLayers (?). 
  
-Au niveau pratique, concernant la création de tuile GeoJson, Postgis semble offrir la possibilité de découper des géométries en fonction d'un polygone donnée ("clip") à l'aide de la fonction ''[[https://postgis.net/docs/ST_Intersection.html| ST_Intersection()]]''.+Au niveau pratique, concernant la création de tuiles GeoJson, Postgis semble offrir la possibilité de découper des géométries en fonction d'un polygone donnée ("clip") à l'aide de la fonction ''[[https://postgis.net/docs/ST_Intersection.html| ST_Intersection()]]''.
 Il semble également possible d'utiliser [[https://github.com/5chdn/geojson-map-tiler/blob/master/tiler/geojson.rb#L104C164-L104C167| l'utilitaire ogr2ogr]] qui permet de générer du GeoJson en [[https://gdal.org/programs/ogr2ogr.html#cmdoption-ogr2ogr-clipsrc|redécoupant les géométrie données en fonction du contour d'une bbox]]. Il semble également possible d'utiliser [[https://github.com/5chdn/geojson-map-tiler/blob/master/tiler/geojson.rb#L104C164-L104C167| l'utilitaire ogr2ogr]] qui permet de générer du GeoJson en [[https://gdal.org/programs/ogr2ogr.html#cmdoption-ogr2ogr-clipsrc|redécoupant les géométrie données en fonction du contour d'une bbox]].
  
 Exemples de code pour créer des tuiles GeoJson : Exemples de code pour créer des tuiles GeoJson :
-  * [[https://github.com/tobinbradley/dirt-simple-postgis-http-api/blob/master/routes/geojson.js|Dirt-simple-postgis-http-api - GeoJson]] => en réalité, cela ne créé pas un geojson dont les géométrie sont restreinte à la tuile mais renvoie toutes les géométrie intersectant la bbox de la tuile demandée...+  * [[https://github.com/tobinbradley/dirt-simple-postgis-http-api/blob/master/routes/geojson.js|Dirt-simple-postgis-http-api - GeoJson]] => en réalité, cela ne crée pas un geojson dont les géométries sont restreintes à la tuile mais renvoie toutes les géométrie intersectant la bbox de la tuile demandée...
  
 ==== Tuiles Mapbox Vector Tile ==== ==== Tuiles Mapbox Vector Tile ====
Ligne 104: Ligne 104:
 {{ https://prod-api.symphony.is/assets/07-screen-shot-2021-11-14-at-093219-2.webp?450}} {{ https://prod-api.symphony.is/assets/07-screen-shot-2021-11-14-at-093219-2.webp?450}}
  
-Comme indiqué précédemment, il peut être utile de simplifier les géométries retournée en réduisant le nombre de chiffre après la virgule des coordonnées en utilisant la fonction Postgis ''[[https://postgis.net/docs/ST_SnapToGrid.html| ST_SnapToGrid()]]''. Pour garder une précision métrique, 5 chiffres après la virgule suffisent : ''ST_SnapToGrid(geom, 0.00001)''.+Comme indiqué précédemment, il peut être utile de simplifier les géométries retournées en réduisant le nombre de chiffres après la virgule des coordonnées en utilisant la fonction Postgis ''[[https://postgis.net/docs/ST_SnapToGrid.html| ST_SnapToGrid()]]''. Pour garder une précision métrique, 5 chiffres après la virgule suffisent : ''ST_SnapToGrid(geom, 0.00001)''.
  
-S'il s'avèrent nécessaire de simplifier des géométries complexes (Communes, Départements, zones de protection...), il faut utiliser des fonctions qui préservent la topologie des géométries pour éviter de créer des géométries invalides. C'est le cas de la fonctions Posgis ''[[https://postgis.net/docs/ST_SimplifyPreserveTopology.html| ST_SimplyPreserveTopology]]''.+S'il s'avère nécessaire de simplifier des géométries complexes (Communes, Départements, zones de protection...), il faut utiliser des fonctions qui préservent la topologie des géométries pour éviter de créer des géométries invalides. C'est le cas de la fonctions Postgis ''[[https://postgis.net/docs/ST_SimplifyPreserveTopology.html| ST_SimplyPreserveTopology]]''.
  
-Le web service retournant ces géométrie doit pouvoir [[https://symphony.is/about-us/blog/boosting-postgis-performance| activer et augmenter la simplification des géométrie en fonction du niveau zoom]]. L'utilisation de la formule suivante dans les requêtes est très intéressante car cela permet de simplifier automatiquement les géométries en fonction du niveau de zoome de façon que les écarts apparaissant entre 2 géométries contiguës ne soient pas visibles : ''ST_SimplyPreserveTopology(geom, 0.7 / (2 ^ <zoom-level>))''.+Le web service retournant ces géométrie doit pouvoir [[https://symphony.is/about-us/blog/boosting-postgis-performance| activer et augmenter la simplification des géométries en fonction du niveau de zoom]]. L'utilisation de la formule suivante dans les requêtes est très intéressante car cela permet de simplifier automatiquement les géométries en fonction du niveau de zoom de façon à ce que les écarts apparaissant entre 2 géométries contiguës ne soient pas visibles : ''ST_SimplyPreserveTopology(geom, 0.7 / (2 ^ <zoom-level>))''.
  
 ==== Framework carto web et tuiles vecteurs ==== ==== Framework carto web et tuiles vecteurs ====
  
-Actuellement, nous utilisons **Leaflet** comme framework carto web. Il est simple, peu verbeux et fournie jusqu'à présent toutes les fonctionnalités carto dont GeoNature à besoin. Malheureusement, il ne supporte pas nativement les tuiles vecteurs. Il est possible d'utiliser un plugin pour cela mais il en existe de nombreux qui ne sont pas tous bien maintenu. Nous listerons les plugins Leaflet existant permettant l'utilisation de tuiles vecteurs, détermineront s'ils sont toujours actifs et évaluerons s'ils sont bien maintenus.+Actuellement, nous utilisons **Leaflet** comme framework de cartographie web. Il est simple, peu verbeux et fournit, jusqu'à présenttoutes les fonctionnalités cartographiques dont GeoNature besoin. Malheureusement, il ne supporte pas nativement les tuiles vecteurs. Il est possible d'utiliser un plugin pour cela mais il en existe de nombreux qui ne sont pas tous bien maintenus. Nous listerons les plugins Leaflet existants, permettant l'utilisation de tuiles vecteurs, et nous déterminerons s'ils sont toujours actifs et évaluerons s'ils sont bien maintenus.
  
-Enfin, nous testerons la solution dont le rendu des tuiles vecteur est le plus performant dans un navigateur web. Même si cela impliquerait une modification importante du code de GeoNature, les possibilités offertes par ces récents frameworks supportant de nouvelle techniques d'affichage web pourraient s'avérer valoir l'investissement. Nous testerons l'utilisation de tuiles vecteur avec le plus intéressant et évaluerons sa facilité d'intégration avec Angular.+Enfin, nous testerons la solution dont le rendu des tuiles vecteur est le plus performant dans un navigateur web. Même si cela impliquerait une modification importante du code de GeoNature, les possibilités offertes par ces récents frameworks supportant de nouvelles techniques d'affichage web pourraient s'avérer valoir l'investissement. Nous testerons l'utilisation de tuiles vecteur avec le plus intéressant et évaluerons sa facilité d'intégration avec Angular.
  
 === Besoins === === Besoins ===
Ligne 122: Ligne 122:
   * être le plus performant possible pour le rendu de tuiles vecteur dans un navigateur web   * être le plus performant possible pour le rendu de tuiles vecteur dans un navigateur web
  
-Dans le cas d'un framework carto différent de Leaflet, en plus des besoins listés précédemment, il doit :+Dans le cas d'un framework cartographique différent de Leaflet, en plus des besoins listés précédemment, il doit :
   * supporter l'affichage de tuiles "raster" au format TMS fournie par OpenStreetMap   * supporter l'affichage de tuiles "raster" au format TMS fournie par OpenStreetMap
   * supporter l'affichage de données issues de web service WMS   * supporter l'affichage de données issues de web service WMS
Ligne 129: Ligne 129:
  
 === Plugins Leaflet ajoutant le support des tuiles vecteur === === Plugins Leaflet ajoutant le support des tuiles vecteur ===
-Leaflet consacre [[https://leafletjs.com/plugins.html#vector-tiles| une catégorie pour les plugins]] apportant le support des tuiles vecteurs. Les plugins non libres ou s'appuyant sur une API propriétaire ne seront pas pris en compte.+Leaflet consacre [[https://leafletjs.com/plugins.html#vector-tiles| une catégorie pour les plugins]] apportant le support des tuiles vecteur. Les plugins non libres ou s'appuyant sur une API propriétaire ne seront pas pris en compte.
 Liste des plugins apportant le support des tuiles MVT ou GeoJson à Leaflet classé dans l'ordre décroissant d'activité/maintenance : Liste des plugins apportant le support des tuiles MVT ou GeoJson à Leaflet classé dans l'ordre décroissant d'activité/maintenance :
 ^  Nom                            Dépôt                                                                                                    ^ Formats supportés  ^  Dernière version  ^  Date dernière release  ^  Date dernier commit  ^ Licence                ^ ^  Nom                            Dépôt                                                                                                    ^ Formats supportés  ^  Dernière version  ^  Date dernière release  ^  Date dernier commit  ^ Licence                ^
Ligne 139: Ligne 139:
 | **Hoverboard**                 | [[https://github.com/summer4096/hoverboard|summer4096/hoverboard]]                                        | MVT, GeoJson        v1.1.3            |  27 mars 2015            13 mai 2015          | ?                      | | **Hoverboard**                 | [[https://github.com/summer4096/hoverboard|summer4096/hoverboard]]                                        | MVT, GeoJson        v1.1.3            |  27 mars 2015            13 mai 2015          | ?                      |
  
-L'utilisation de [[https://leafletjs.com/| Leafleft]] avec le plugin [[https://github.com/maplibre/maplibre-gl-leaflet|Maplibre-Gl-Leaflet]] est la solution qu'il faudrait retenir car la communauté autour de MapLibre (fork de Mapbox GL JS) semble être bien active. Ceci dit les plugins [[https://gitlab.com/jkuebart/Leaflet.VectorTileLayer/| jkuebart/Leaflet.VectorTileLayer]] et [[https://github.com/protomaps/protomaps-leaflet| protomaps/protomaps-leaflet]] semblent être des solutions envisageables.+L'utilisation de [[https://leafletjs.com/| Leafleft]] avec le plugin [[https://github.com/maplibre/maplibre-gl-leaflet|Maplibre-Gl-Leaflet]] est la solution qu'il faudrait retenir car la communauté autour de MapLibre (fork de Mapbox GL JS) semble être bien active. Ceci ditles plugins [[https://gitlab.com/jkuebart/Leaflet.VectorTileLayer/| jkuebart/Leaflet.VectorTileLayer]] et [[https://github.com/protomaps/protomaps-leaflet| protomaps/protomaps-leaflet]] semblent être des solutions envisageables.
  
 === Framework carto web avec support natif des tuiles vecteur === === Framework carto web avec support natif des tuiles vecteur ===
-Les frameworks carto web suivant possèdent un support des tuiles vecteurs sans l'ajout d'un plugin sont +Les frameworks cartographiques web suivant possèdent un support des tuiles vecteur sans l'ajout d'un plugin : 
   - [[https://maplibre.org/maplibre-gl-js/docs/| MapLibre GL]]   - [[https://maplibre.org/maplibre-gl-js/docs/| MapLibre GL]]
   - [[https://openlayers.org/| OpenLayers]]   - [[https://openlayers.org/| OpenLayers]]
  
 === Conclusion === === Conclusion ===
-Nous testerons l'utilisation du framework MapLibre GL car son développement a été basé dès l'origine sur l'utilisation de tuiles vecteur. C'est celui qui est sensé nous offrir les meilleures performances lors de l'utilisation de tuiles vecteur.+Nous testerons l'utilisation du framework MapLibre GL car son développement a été basé dès l'origine sur l'utilisation de tuiles vecteur. Il est censé offrir les meilleures performances lors de l'utilisation de tuiles vecteur.
  
-Ce test sera réalisé en développant [[https://github.com/cbn-alpin/gn_module_syntests/tree/develop| un module GeoNature spécifique "Syntests"]]. Cela permettra d'évaluer la facilité d'intégration du framework à Angular. Nous pourrons aussi concentrer nos sur l'optimisation des performances du rendu des observations issues de la table ''synthese'' de GeoNature sans être contraint par l'implémentation actuelle du module "Synthese".+Ce test sera réalisé en développant [[https://github.com/cbn-alpin/gn_module_syntests/tree/develop| un module GeoNature spécifique "Syntests"]]. Cela permettra d'évaluer la facilité d'intégration du framework à Angular. Nous pourrons aussi concentrer nos efforts sur l'optimisation des performances du rendu des observations issues de la table ''synthese'' de GeoNature sans être contraints par l'implémentation actuelle du module "Synthese".
 ==== Tests de serveurs de tuiles vecteurs ==== ==== Tests de serveurs de tuiles vecteurs ====
  
Ligne 166: Ligne 166:
   * [[https://github.com/mapbox/awesome-vector-tiles?tab=readme-ov-file#servers| Voir la liste de  serveurs de tuiles vecteurs]] maintenue par Mapbox   * [[https://github.com/mapbox/awesome-vector-tiles?tab=readme-ov-file#servers| Voir la liste de  serveurs de tuiles vecteurs]] maintenue par Mapbox
  
-L'utilisation de ces serveurs de tuiles vecteurs nécessite de les héberger sur son propre serveur. Cela implique :+L'utilisation de ces serveurs de tuiles vecteur nécessite de les héberger sur son propre serveur. Cela implique :
   * l'installation, la configuration et la maintenance du serveur   * l'installation, la configuration et la maintenance du serveur
   * de disposer de suffisamment d'espace disque : de quelques dizaines à plusieurs centaines de Go suivant les niveaux de zoom supportés et la taille de la carte à rendre (pays, continent, planète).   * de disposer de suffisamment d'espace disque : de quelques dizaines à plusieurs centaines de Go suivant les niveaux de zoom supportés et la taille de la carte à rendre (pays, continent, planète).
  
-Dans le cadre de GeoNature, l'utilisation de ce type de serveurs peut être intéressant si le framework carto web utilisé pour gérer les cartes supporte bien les tuiles vecteurs. Cela ouvre la voie de nouveaux usages : vue 3D (intéressant pour mieux visualiser les pentes, vallée...), changement de langue des textes de la carte, changement de style instantané du fond carto en fonction des usages (nuit, nature, ...). Dans le cas contraire, il vaut mieux privilégier l'utilisation de tuiles raster.+Dans le cadre de GeoNature, l'utilisation de ce type de serveurs peut être intéressant si le framework cartographique web utilisé pour gérer les cartes supporte bien les tuiles vecteurs. Cela ouvre la voie à de nouveaux usages : vue 3D (intéressant pour mieux visualiser les pentes, vallées...), changement de langue des textes de la carte, changement de style instantané du fond cartographique en fonction des usages (nuit, nature, ...). Dans le cas contraire, il vaut mieux privilégier l'utilisation de tuiles raster.
  
 Pour les cartes de GeoNature, il pourrait être intéressant de **fournir des tuiles vecteurs pour le fond cartographique spécialement conçu pour les zones non urbanisés**. Ce style d'affichage est nommé [[https://openmaptiles.org/styles/#maptiler-terrain|"terrain" ou "outdoor"]]. Il existe [[https://github.com/openmaptiles/maptiler-terrain-gl-style|des fichiers de styles open source pour ce type d'affichage]]. Pour les cartes de GeoNature, il pourrait être intéressant de **fournir des tuiles vecteurs pour le fond cartographique spécialement conçu pour les zones non urbanisés**. Ce style d'affichage est nommé [[https://openmaptiles.org/styles/#maptiler-terrain|"terrain" ou "outdoor"]]. Il existe [[https://github.com/openmaptiles/maptiler-terrain-gl-style|des fichiers de styles open source pour ce type d'affichage]].
Ligne 181: Ligne 181:
 === Serveurs de tuiles pour couches de données spécifiques === === Serveurs de tuiles pour couches de données spécifiques ===
  
-Ces serveurs s'appuie sur une base de données Postgis existante pour générer les tuiles vecteurs. La configuration de ces serveurs prévoie de pouvoir associer une requête SQL à chaque couche de tuiles vecteurs que l'on souhaite générer.+Ces serveurs s'appuie sur une base de données Postgis existante pour générer les tuiles vecteur. La configuration de ces serveurs prévoit de pouvoir associer une requête SQL à chaque couche de tuiles vecteur que l'on souhaite générer.
  
 Serveurs de couches intéressant : Serveurs de couches intéressant :
Ligne 188: Ligne 188:
   * [[https://github.com/CrunchyData/pg_tileserv | pg_tileserver]]   * [[https://github.com/CrunchyData/pg_tileserv | pg_tileserver]]
  
-Dans le cadre de GeoNature, ce type de serveur peut être intéressant à installer si l'on souhaite générer des couches de tuiles vecteurs spécifiques à des données hébergées dans la base. Par exemple, les différents types de zones géo présentent dans la table ''ref_geo.l_areas'' pourraient être proposé sous forme de web services de tuiles vecteurs activable ou pas en fonction des besoins. Il est possible d'imaginer aussi des web services d’agrégation de données basés sur des vues : intensité de prospection, nombre d'observations, diversité spécifiques...+Dans le cadre de GeoNature, ce type de serveur peut être intéressant à installer si l'on souhaite générer des couches de tuiles vecteur spécifiques à des données hébergées dans la base. Par exemple, les différents types de zones géographiques présents dans la table ''ref_geo.l_areas'' pourraient être proposés sous forme de web services de tuiles vecteur activable ou pas en fonction des besoins. Il est possible d'imaginer aussi des web services d’agrégation de données basés sur des vues : intensité de prospection, nombre d'observations, diversité spécifique...
  
-Par contre, les services de tuiles en question ne doivent pas être assujetti à l'utilisation de filtres dépendant du choix de l'utilisateur via l'interface. Il faut pouvoir fournir les tuiles de la même façon quelque soit la personne en faisant la demande...+Par contre, les services de tuiles en question ne doivent pas être assujetti à l'utilisation de filtres dépendant du choix de l'utilisateur via l'interface. Il faut pouvoir fournir les tuiles de la même façon quelle que soit la personne en faisant la demande...
  
-==== Web service de tuiles vecteurs sur mesure ==== +==== Web service de tuiles vecteur sur mesure ==== 
-Dans notre cas, il semble **plus intéressant de construire des web services de tuiles vecteurs en s'appuyant sur les fonctionnalités offertes par Postgis**. Les données renvoyées dans les tuiles de la Synthese" seront dépendantes :+Dans notre cas, il semble **plus intéressant de construire des web services de tuiles vecteur en s'appuyant sur les fonctionnalités offertes par Postgis**. Les données renvoyées dans les tuiles de la Synthèse" seront dépendantes :
   * de l'utilisateur en faisant la demande   * de l'utilisateur en faisant la demande
   * de nombreux filtres au choix de l'utilisateur   * de nombreux filtres au choix de l'utilisateur
  
-Posgis fournit des fonctions permettant de faciliter la création de tuile MVT : +Postgis fournit des fonctions permettant de faciliter la création de tuile MVT : 
-  * ''ST_TileEnvelope(z, x, y)'' ([[https://postgis.net/docs/ST_TileEnvelope.html|doc]]) : permet de crée un polygone rectangulaire (bbox) dans le SRID 3857 en fonction du niveau de zoom, du x et du y d'une URL d'un [[https://wiki.openstreetmap.org/wiki/TMS|web service TMS]].+  * ''ST_TileEnvelope(z, x, y)'' ([[https://postgis.net/docs/ST_TileEnvelope.html|doc]]) : permet de créer un polygone rectangulaire (bbox) dans le SRID 3857 en fonction du niveau de zoom, du x et du y d'une URL d'un [[https://wiki.openstreetmap.org/wiki/TMS|web service TMS]].
   * ''ST_AsMVTGeom()'' ([[https://postgis.net/docs/ST_AsMVTGeom.html|doc]]) : transforme une géométrie dans l'espace de coordonnées d'une tuile MVT (Mapbox Vector Tile), en la coupant aux limites de la tuile si nécessaire.   * ''ST_AsMVTGeom()'' ([[https://postgis.net/docs/ST_AsMVTGeom.html|doc]]) : transforme une géométrie dans l'espace de coordonnées d'une tuile MVT (Mapbox Vector Tile), en la coupant aux limites de la tuile si nécessaire.
   * ''UNION'' et ''ST_AsMVTGeom()'' : permettent de [[https://medium.com/@shahzadbacha.gis/composite-mvt-tiles-with-postgis-4b30d6c9f510| stocker plusieurs couches]] (//layers//) de géométries "MVT" dans la même tuile.   * ''UNION'' et ''ST_AsMVTGeom()'' : permettent de [[https://medium.com/@shahzadbacha.gis/composite-mvt-tiles-with-postgis-4b30d6c9f510| stocker plusieurs couches]] (//layers//) de géométries "MVT" dans la même tuile.
   * ''ST_AsMVT()'' ([[https://postgis.net/docs/ST_AsMVT.html|doc]]) : fonction d'agrégation qui renvoie une représentation binaire Mapbox Vector Tile d'un ensemble de lignes correspondant à une couche de tuiles.    * ''ST_AsMVT()'' ([[https://postgis.net/docs/ST_AsMVT.html|doc]]) : fonction d'agrégation qui renvoie une représentation binaire Mapbox Vector Tile d'un ensemble de lignes correspondant à une couche de tuiles. 
  
-Exemple de requêtes créant une tuile vecteur basée sur la table ''ref_geo.l_areas'' de GeoNature pour les valeurs zoom = 16, x = 33877 et y = 23672  : <code sql>+Exemple de requête créant une tuile vecteur basée sur la table ''ref_geo.l_areas'' de GeoNature pour les valeurs zoom = 16, x = 33877 et y = 23672  : <code sql>
 WITH bounds AS ( WITH bounds AS (
  SELECT ST_TileEnvelope(16, 33877, 23672) AS envelope  SELECT ST_TileEnvelope(16, 33877, 23672) AS envelope
Ligne 230: Ligne 230:
     * [[https://medium.com/@shahzadbacha.gis/composite-mvt-tiles-with-postgis-4b30d6c9f510| Composite MVT tiles with Postgis]]     * [[https://medium.com/@shahzadbacha.gis/composite-mvt-tiles-with-postgis-4b30d6c9f510| Composite MVT tiles with Postgis]]
  
-==== Jeu de données des tests ====+==== Principe des tests de performance ====
  
-Pour nos testsnous avons utilisé la base du SINP AURA qui comprend 23,5 millions d'observations dans la table ''synthese'' et 378 millions de lignes dans la table ''cor_area_synthese''.+Nous avons réalisé plusieurs tests à l'aide du module [[https://github.com/cbn-alpin/gn_module_syntests/tree/develop| un module GeoNature spécifique "Syntests"]]. Il nous a permis d'encapsuler rapidement l’exécution de requêtes SQL afin de comparer leur rendu sous forme de tuiles vecteur affichées à l'aide du framework carto web Maplibre GL.
  
 +Nous avons également utilisé les données de la base du SINP AURA qui comprend 23,5 millions d'observations dans la table ''synthese'' et 378 millions de lignes dans la table ''cor_area_synthese''. Nous avons pu ainsi tester l'efficacité des tuiles vecteurs sur une base de données comprenant un nombre conséquent d'observations.
  
-==== Comparaison SRID 2154 et 4326 ==== 
  
-Les [[#annexe_1_-_comparaison_srid_4326_et_2154| résultats de la comparaison]] du nombre de données récupérés et des temps d'obtention entre l'utilisation du SRID 4326 et 2154 ne montre pas de différences majeures. Mais l'utilisation du SRID 4326 semble légèrement plus rapide et en outre demande moins de traitement pour son utilisation avec  le format GeoJson.+=== Comparaison SRID 2154 et 4326 === 
 + 
 +Les [[#annexe_1_-_comparaison_srid_4326_et_2154| résultats de la comparaison]] du nombre de données récupérées et des temps d'obtention entre l'utilisation du SRID 4326 et 2154 ne montrent pas de différences majeures. Mais l'utilisation du SRID 4326 semble légèrement plus rapide et  demande moins de traitement pour son utilisation avec le format GeoJson.
  
 //Conclusion// : **privilégier le SRID 4326** pour les champs de type géométrie. //Conclusion// : **privilégier le SRID 4326** pour les champs de type géométrie.
  
-==== Comparaison opérateur && et st_intersects avec index GIST ====+=== Comparaison opérateur && et st_intersects avec index GIST ===
  
 Le test de [[#annexe_2_-_comparaison_operateur_et_st_intersects_avec_index_gist| comparaison des opérateurs ''&&'' et de la fonction ''ST_Intersects'' avec un index GIST]] montre que l'opérateur ''&&'' est plus rapide. Cependant il ne peut être utilisé qu'avec des géométries de type "bounding box" (des rectangles). Le test de [[#annexe_2_-_comparaison_operateur_et_st_intersects_avec_index_gist| comparaison des opérateurs ''&&'' et de la fonction ''ST_Intersects'' avec un index GIST]] montre que l'opérateur ''&&'' est plus rapide. Cependant il ne peut être utilisé qu'avec des géométries de type "bounding box" (des rectangles).
Ligne 247: Ligne 249:
 //Conclusion// : **privilégier l'opérateur ''&&''** quand c'est possible sinon utiliser ''st_intersects''. //Conclusion// : **privilégier l'opérateur ''&&''** quand c'est possible sinon utiliser ''st_intersects''.
  
- +=== Comparaison opérateur && et st_intersects avec index BRIN ===
- +
-==== Comparaison opérateur && et st_intersects avec index BRIN ====+
  
 Le [[#annexe_3_-_comparaison_operateur_et_st_intersects_avec_index_brin| test d'utilisation d'un index de type BRIN]] sans trier les lignes sur la colonne géométrique utilisée montre des temps d'exécution plus long qu'avec l'index GIST. Ces temps sont plus longs pour l'opérateur ''&&'' comme pour la fonction ''ST_Intersects''. Le [[#annexe_3_-_comparaison_operateur_et_st_intersects_avec_index_brin| test d'utilisation d'un index de type BRIN]] sans trier les lignes sur la colonne géométrique utilisée montre des temps d'exécution plus long qu'avec l'index GIST. Ces temps sont plus longs pour l'opérateur ''&&'' comme pour la fonction ''ST_Intersects''.
Ligne 255: Ligne 255:
 //Conclusion// : **utiliser un index GIST** sur le champ contenant les géométries des observations de la Synthese. //Conclusion// : **utiliser un index GIST** sur le champ contenant les géométries des observations de la Synthese.
  
-==== Comparaison index GIST et SP-GIST ==== +=== Comparaison index GIST et SP-GIST === 
-En suivant les informations fournies par ce document "[[https://public.dalibo.com/exports/formation/manuels/modules/j5/j5.handout.pdf| Dalibo - Indexation avancée]]", nous avons mis en place un index de type SP-GIST sur le champ géométrie de la Synthese. Les [[#annexe_4_-_comparaison_index_gist_et_sp-gist| résultats de la requête utilisée]] montre un léger avantage pour l'index de type SP-GIST.+En suivant les informations fournies par ce document "[[https://public.dalibo.com/exports/formation/manuels/modules/j5/j5.handout.pdf| Dalibo - Indexation avancée]]", nous avons mis en place un index de type SP-GIST sur le champ géométrie de la Synthèse. Les [[#annexe_4_-_comparaison_index_gist_et_sp-gist| résultats de la requête utilisée]] montre un léger avantage pour l'index de type SP-GIST.
  
 //Conclusion// : **utiliser un index SP-GIST** pour améliorer les performances sur la colonne géométrie de la Synthese. //Conclusion// : **utiliser un index SP-GIST** pour améliorer les performances sur la colonne géométrie de la Synthese.
  
-==== Comparaison agrégation via table relation et via intersection ====+=== Comparaison agrégation via table relation et via intersection ===
  
 La [[#annexe_5_-_comparaison_agregation_via_table_relation_et_via_intersection| comparaison de l'agrégation des observations]] via l'utilisation de la table ''cor_area_synthese'' ou via l'opération spatiale ''&&'' et des index SP-GIST mais bien en évidence qu'il est plus performant d'utiliser une table relationnelle pour pré-stocker les intersections. La [[#annexe_5_-_comparaison_agregation_via_table_relation_et_via_intersection| comparaison de l'agrégation des observations]] via l'utilisation de la table ''cor_area_synthese'' ou via l'opération spatiale ''&&'' et des index SP-GIST mais bien en évidence qu'il est plus performant d'utiliser une table relationnelle pour pré-stocker les intersections.
Ligne 267: Ligne 267:
  
 Il faut donc maintenir l'utilisation de la table ''cor_area_synthese'' et chercher des solutions visant à optimiser ce mécanisme : Il faut donc maintenir l'utilisation de la table ''cor_area_synthese'' et chercher des solutions visant à optimiser ce mécanisme :
-  * Ajout d'une colonne indiquant le code du type de zone geo dans la table cor_area_synthese : ''area_type_code'' +  * Ajout d'une colonne indiquant le code du type de zone géographique dans la table cor_area_synthese : ''area_type_code'' 
-    * Partitionnement de la table basé sur le type de zone geo+    * Partitionnement de la table basé sur le type de zone géographique
   * Création d'une table de relation spécialisée pour les mailles M10 : ''id_synthese'', ''id_area''   * Création d'une table de relation spécialisée pour les mailles M10 : ''id_synthese'', ''id_area''
-  * Mise en cache du nombre d'observation par maille M10 : ''id_area'', ''observation_nbr'' +  * Mise en cache du nombre d'observations par maille M10 : ''id_area'', ''observation_nbr''
  
 //Conclusion// : **pré-calculer et stocker dans une table les intersections de géométries**. //Conclusion// : **pré-calculer et stocker dans une table les intersections de géométries**.
  
-==== Test de l'ajout d'un champ area_type_code à la table cor_area_synthese ====+=== Test de l'ajout d'un champ area_type_code à la table cor_area_synthese ===
 La table ''cor_area_synthese'' comprenant un très grand nombre de lignes (378 millions dans notre cas), il pourrait s'avérer plus performant de permettre à Postgresql d'éliminer un grand nombre d'entre elles en spécifiant le type de la géométrie liée. Pour cela, nous proposons [[#annexe_6_-_ajout_champ_area_type_code_a_cor_area_synthese| d'ajouter un champ ''area_type_code'']]. Nous avons également tester l'utilisation de [[#annexe_7_-_test_de_differents_index_sur_area_type_code| différents index sur ce champ]]. La table ''cor_area_synthese'' comprenant un très grand nombre de lignes (378 millions dans notre cas), il pourrait s'avérer plus performant de permettre à Postgresql d'éliminer un grand nombre d'entre elles en spécifiant le type de la géométrie liée. Pour cela, nous proposons [[#annexe_6_-_ajout_champ_area_type_code_a_cor_area_synthese| d'ajouter un champ ''area_type_code'']]. Nous avons également tester l'utilisation de [[#annexe_7_-_test_de_differents_index_sur_area_type_code| différents index sur ce champ]].
  
 //Conclusion// : **l'utilisation du champ ''area_type_code'' améliore les performances lorsqu'il est utilisé avec un index '''btree(area_type_code, id_area)''**. //Conclusion// : **l'utilisation du champ ''area_type_code'' améliore les performances lorsqu'il est utilisé avec un index '''btree(area_type_code, id_area)''**.
-==== Test vue matérialisée pour relations entre observations ''synthese'' et mailles M10 ===+ 
 +=== Test vue matérialisée pour relations entre observations ''synthese'' et mailles M10 ===
 La [[#annexe_9_-_creation_vue_materialisee_cor_m10_synthese| création d'une vue matérialisée spécialisée pour stocker les relations entre observations ''synthese'' et mailles M10]] a permis de [[#annexe_10_-_test_utilisation_de_la_table_cor_m10_synthese| tester son utilisation]]. Nous avons constaté des résultats très proches de ceux obtenus avec le simple ajout du champ ''area_type_code'' sur la table ''cor_area_synthese''. La [[#annexe_9_-_creation_vue_materialisee_cor_m10_synthese| création d'une vue matérialisée spécialisée pour stocker les relations entre observations ''synthese'' et mailles M10]] a permis de [[#annexe_10_-_test_utilisation_de_la_table_cor_m10_synthese| tester son utilisation]]. Nous avons constaté des résultats très proches de ceux obtenus avec le simple ajout du champ ''area_type_code'' sur la table ''cor_area_synthese''.
  
 //Conclusion// : **continuer d'utiliser ''cor_area_synthese''** plutôt que des vues matérialisées par type de zones géoégraphiques. //Conclusion// : **continuer d'utiliser ''cor_area_synthese''** plutôt que des vues matérialisées par type de zones géoégraphiques.
-==== Test de vues matérialisées pour mettre en cache le nombre d'observations ''synthese'' par mailles ==== 
  
-La [[#annexe_11_-_creation_vue_materialisee_m10_observation_nbr| création d'une première vue matérialisée]] ''m10_observation_nbr'' a permis de en cache le nombre d'observations ''synthese'' par maille M10. La [[#annexe_12_-_creation_vue_materialisee_observation_nbr| création d'une seconde vue matérialisée]] ''observation_nbr'' a permis de mettre en cache le nombre d'observations ''synthese'' pour les différents types de mailles (M10, M5 et M1).+=== Test de vues matérialisées pour mettre en cache le nombre d'observations ''synthese'' par mailles ===
  
-Cela nous a permis de [[#annexe_13_-_tests_utilisation_vue_materialisee_m10_observation_nbrtester l'utilisation d'une vue contenant un nombre réduit de données]], uniquement pour les mailles 10km (table ''m10_observation_nbr''), vis à vis de [[#annexe_14_-_tests_utilisation_vue_materialisee_observation_nbrla vue contenant la mise en cache du nombre d'observation pour l'ensemble des mailles]] (table ''observation_nbr'').+La [[#annexe_11_-_creation_vue_materialisee_m10_observation_nbrcréation d'une première vue matérialisée]] ''m10_observation_nbr'' a permis de mettre en cache le nombre d'observations ''synthese'' par maille M10. La [[#annexe_12_-_creation_vue_materialisee_observation_nbrcréation d'une seconde vue matérialisée]] ''observation_nbr'' a permis de mettre en cache le nombre d'observations ''synthese'' pour les différents types de mailles (M10, M5 et M1).
  
-Nous constatons bien que les requêtes effectuées sur vue contenant la mise en cache de calcul seulement pour les mailles M10 est plus rapide que celle contenant l'ensemble des mailles. Mais dans les 2 cas et pour l'ensemble des requêtes, nous obtenons de très bon temps de réponse (moins de 0,1s). +Cela nous permis de [[#annexe_13_-_tests_utilisation_vue_materialisee_m10_observation_nbr| tester l'utilisation d'une vue contenant un nombre réduit de données]]uniquement pour les mailles 10km (table ''m10_observation_nbr''), vis à vis de [[#annexe_14_-_tests_utilisation_vue_materialisee_observation_nbr| la vue contenant la mise en cache du nombre d'observations pour l'ensemble des mailles]] (table ''observation_nbr'').
-Même si l'utilisation d'une vue matérialisée spécifique a un type de maille est plus rapideil semble plus intéressant d'utiliser la vue matérialise prenant en compte l'ensemble des mailles car le rafraîchissement des données d'une seule vue matérialisée est plus simple à gérer.+
  
-Par ailleurs, la mise en cache de calcul sur de très grande quantité de données et donc une solution très satisfaisante pour générer des tuiles vecteurs. Leur affichage dans l'interface est très fluide. Malheureusement, cette méthode peut être difficilement appliquée sur le module Synthese qui utilise de nombreux filtres spécifiques et dont les résultats sont également dépendant des droits de l'utilisateur.+Nous constatons bien que les requêtes effectuées sur la vue contenant la mise en cache de calcul seulement pour les mailles M10, est plus rapide que celle contenant l'ensemble des mailles. Mais dans les 2 cas et pour l'ensemble des requêtes, nous obtenons de très bon temps de réponse (moins de 0,1s). 
 +Même si l'utilisation d'une vue matérialisée spécifique à un type de maille est plus rapide, il semble plus intéressant d'utiliser la vue matérialise prenant en compte l'ensemble des mailles car le rafraîchissement des données d'une seule vue matérialisée est plus simple à gérer. 
 + 
 +Par ailleurs, la mise en cache de calcul sur de très grandes quantités de données est donc une solution très satisfaisante pour générer des tuiles vecteurs. Leur affichage dans l'interface est très fluide. Malheureusement, cette méthode peut être difficilement appliquée sur le module Synthese qui utilise de nombreux filtres spécifiques et dont les résultats sont également dépendant des droits de l'utilisateur.
  
 //Conclusion// //Conclusion//
   * **mettre les calculs (ex. ''COUNT'') en "cache" dès que possible**   * **mettre les calculs (ex. ''COUNT'') en "cache" dès que possible**
   * trouver le juste milieu entre performance et facilité de maintenance   * trouver le juste milieu entre performance et facilité de maintenance
-==== Test de la création d'une table "cor_area_synthese" partitionnée ====+ 
 +=== Test de la création d'une table "cor_area_synthese" partitionnée ===
 //Ressources// : //Ressources// :
     * [[https://blog.capdata.fr/index.php/postgresql-evolution-du-partitionnement-de-9-6-a-12-1-2/| PostgreSQL : évolution du partitionnement de 9.6 à 12 (1/2)]] : Capdata team, 14 novembre 2019.     * [[https://blog.capdata.fr/index.php/postgresql-evolution-du-partitionnement-de-9-6-a-12-1-2/| PostgreSQL : évolution du partitionnement de 9.6 à 12 (1/2)]] : Capdata team, 14 novembre 2019.
     * [[https://blog.capdata.fr/index.php/postgresql-evolution-du-partitionnement-de-9-6-a-12-2-2/| PostgreSQL : évolution du partitionnement de 9.6 à 12 (2/2)]] : Capdata team, 22 novembre 2019.      * [[https://blog.capdata.fr/index.php/postgresql-evolution-du-partitionnement-de-9-6-a-12-2-2/| PostgreSQL : évolution du partitionnement de 9.6 à 12 (2/2)]] : Capdata team, 22 novembre 2019. 
  
-Nous avons [[#annexe_15_-_creation_d_une_table_cor_area_synthese_partitionnee| créée une table partitionnée ''cor_area_synthese'']]. Cela nous a permis [[#annexe_16_-_infos_sur_table_cor_area_synthese_partitionnee| d'obtenir des informations sur sa composition]], [[#annexe_17_-_test_d_utilisation_d_une_table_cor_area_synthese_partitionnee| effectuer des tests]] dessus et mieux appréhender le principe du partitionnement.+Nous avons [[#annexe_15_-_creation_d_une_table_cor_area_synthese_partitionnee| créé une table partitionnée ''cor_area_synthese'']]. Cela nous a permis [[#annexe_16_-_infos_sur_table_cor_area_synthese_partitionnee| d'obtenir des informations sur sa composition]], [[#annexe_17_-_test_d_utilisation_d_une_table_cor_area_synthese_partitionnee| effectuer des tests]] dessus et mieux appréhender le principe du partitionnement.
  
-Les résultats sur la mise en cache des données pour l'ensemble des mailles vis à vis d'un seul type (M10) a montré que la requête s'exécutant sur un seul type était pls rapide. Le partitionnement subdivisant la table initiale en une multitude de table par type, nous pouvons penser que cela pourra améliorer les résultats.+Les résultats sur la mise en cache des données pour l'ensemble des mailles vis à vis d'un seul type (M10) a montré que la requête s'exécutant sur un seul type était plus rapide. Le partitionnement subdivisant la table initiale en une multitude de table par type, nous pouvons penser que cela pourra améliorer les résultats.
  
 //Notes// : //Notes// :
-  * Il n'est pas possible de réutiliser une table standard pour la partitionnée, il faut recréer une nouvelle table partitionnée dés sa création.+  * Il n'est pas possible de réutiliser une table standard pour la partitionnée, il faut recréer une nouvelle table partitionnée dès sa création.
   * Il est nécessaire d'ajouter une colonne ''area_type_code'' pour l'utiliser comme origine du partitionnement.   * Il est nécessaire d'ajouter une colonne ''area_type_code'' pour l'utiliser comme origine du partitionnement.
   * Toutes les colonnes présentes dans les partitions doivent faire partie de la clé primaire.   * Toutes les colonnes présentes dans les partitions doivent faire partie de la clé primaire.
-  * Il peut être intéressant d'activer le partitionnement d'une table lorsque la taille de celle-ci dépasse la quantité de mémoire vive disponible...+  * Il peut être intéressant d'activer le partitionnement d'une table lorsque la taille de celle-ci dépasse la quantité de mémoire vive disponible.
  
 //Conclusions// //Conclusions//
Ligne 316: Ligne 318:
   * Il est **envisageable de gérer le partitionnement de la table ''cor_area_synthese'' sans toucher au code de GeoNature** à partir du moment où la table ''cor_area_synthese'' possède un champ ''area_type_code''.   * Il est **envisageable de gérer le partitionnement de la table ''cor_area_synthese'' sans toucher au code de GeoNature** à partir du moment où la table ''cor_area_synthese'' possède un champ ''area_type_code''.
  
-==== Test de la fonction st_subdivide() ====+=== Test de la fonction st_subdivide() ===
 Dans le cadre de la création d'une table ''cor_area_synthese'' partitionnée, nous avons utilisé la fonction Postgis [[https://postgis.net/docs/ST_Subdivide.html| st_subdivide()]] afin de créer une table contenant une ensemble de géométries simplifiées pour chaque géométrie complexe (toutes sauf mailles) présentent dans la table ''l_areas''. Dans le cadre de la création d'une table ''cor_area_synthese'' partitionnée, nous avons utilisé la fonction Postgis [[https://postgis.net/docs/ST_Subdivide.html| st_subdivide()]] afin de créer une table contenant une ensemble de géométries simplifiées pour chaque géométrie complexe (toutes sauf mailles) présentent dans la table ''l_areas''.
  
-Les intersections effectuées à l'aide de la fonction [[https://postgis.net/docs/ST_Intersects.html| st_intersects()]] se sont avérées largement plus rapide lorsqu'elles sont exécutées sur les géométries simplifiées de cette table et cela même si elles en contient un plus grand nombre. Voir [[https://blog.cleverelephant.ca/2019/11/subdivide.html| cette article de Paul Ramsey sur le sujet]].+Les intersections effectuées à l'aide de la fonction [[https://postgis.net/docs/ST_Intersects.html| st_intersects()]] se sont avérées largement plus rapides lorsqu'elles sont exécutées sur les géométries simplifiées de cette table et cela même si elles en contient un plus grand nombre. Voir [[https://blog.cleverelephant.ca/2019/11/subdivide.html| cette article de Paul Ramsey sur le sujet]].
  
 //Conclusion// : **utiliser st_intersects() sur des géométries complexes simplifiées via st_subdivide()** //Conclusion// : **utiliser st_intersects() sur des géométries complexes simplifiées via st_subdivide()**
  
-==== Test utilisation table "cor_area_synthese" sans lien vers la "synthese" ====+=== Test utilisation table "cor_area_synthese" sans lien vers la "synthese" ===
  
 Nous avons voulu [[#annexe_18_-_test_utilisation_table_cor_area_synthese_sans_lien_vers_la_synthese| tester une requête s’exécutant seulement sur une seule table]] pour évaluer l'impact des jointures. La requête effectuée seulement sur la table ''cor_area_synthese'' sans relation vers la table ''synthese'' divise le temps d'exécution par 5 ! Nous avons voulu [[#annexe_18_-_test_utilisation_table_cor_area_synthese_sans_lien_vers_la_synthese| tester une requête s’exécutant seulement sur une seule table]] pour évaluer l'impact des jointures. La requête effectuée seulement sur la table ''cor_area_synthese'' sans relation vers la table ''synthese'' divise le temps d'exécution par 5 !
  
-Par ailleurs, nous avons confirmé dans le cadre de l'utilisation d'une autre base de données (Simethis) que des requêtes effectuées sur une seule table contenant des millions d'observations sans aucune jointure permet de générer un GeoJson agrégeant plusieurs millions d'observations (~8 millions) sous forme de mailles sur une surface correspondant à une région pour temps d'exécution de 30 secondes !+Par ailleurs, nous avons confirmé dans le cadre de l'utilisation d'une autre base de données (Simethis) que des requêtes effectuées sur une seule table contenant des millions d'observations sans aucune jointure permet de générer un GeoJson agrégeant plusieurs millions d'observations (~8 millions) sous forme de mailles sur une surface correspondant à une région pour un temps d'exécution de 30 secondes !
  
 //Conclusion// : **réaliser des requêtes sur une seule table bien indexée** ! //Conclusion// : **réaliser des requêtes sur une seule table bien indexée** !
  
-==== Test requêtes Synthese sur VM indexée ====+=== Test requêtes Synthese sur VM indexée === 
 + 
 +Le test précédent a montré une très grande efficacité et nous avons pu la confirmer dans le cadre de l'utilisation d'une autre base de données (Simethis). Les requêtes effectuées sur une seule table contenant des millions d'observations sans aucune jointure permet de générer un GeoJson agrégeant plusieurs millions d'observations (~8 millions) sous forme de mailles sur une surface correspondant à une région pour temps d'exécution de 30 secondes ! 
 + 
 +Nous avons commencer à tester cela en générant une vue matérialisé v_synthese_for_web_app bien indexée. Ensuite, nous avons commencé à modifier le fichier [[https://github.com/PnX-SI/GeoNature/blob/develop/backend/geonature/core/gn_synthese/utils/query_select_sqla.py|query_select_sqla.py]] dans lequel la majorité des requêtes effectuées sur la Synthese sont générées. 
 + 
 +Nous avons lancé des interrogations dans la Synthese en utilisant les filtres par taxon ou par commune. Une fois les requêtes modifiées pour n'utiliser que les champs de la nouvelle VM et après l'utilisation de EXPLAIN pour vérifier leur comportement, nous avons pu constaté une amélioration importante des performances. 
 + 
 +Par ailleurs, cette approche permet de réutiliser un mécanisme déjà en place dans le module Synthese, l'utilisation de la vue ''v_synthese_for_web_app''. L'idée ici est donc de limiter toutes les requêtes à cette vue. //Vue// qui peut devenir //matérialisée// pour exploiter des index sur des bases de données GeoNature à forte volumétrie. Cette solution ne demande finalement qu'un minimum de modifications de code. 
 + 
 +Nous pouvons continuer à utiliser du GeoJson pour le rendu à partir du moment où les observations sont agrégées par mailles dès que leur nombre devient trop important à afficher sur la carte. Une bascule automatique vers ce type d'affichage doit être mise en place également. 
 + 
 +//Conclusion// : **exécuter des requêtes pour le module Synthese uniquement sur une vue matérialisée ''v_synthese_for_web_app'' bien indexée.**
  
 ==== Liste des améliorations ==== ==== Liste des améliorations ====
Ligne 338: Ligne 352:
   * le SRID 4326 pour les intersections   * le SRID 4326 pour les intersections
   * l'opérateur ''&&'' plutôt que ''st_intersects'' si possible   * l'opérateur ''&&'' plutôt que ''st_intersects'' si possible
-  * un index ''SP-GIST'' si les géométrie ne se chevauchant pas, sinon ''GIST'' plutôt que ''BRIN''+  * un index ''SP-GIST'' si les géométrie ne se chevauchent pas, sinon ''GIST'' plutôt que ''BRIN''
     * Pour ''synthese'' => SP-GIST, pour ''l_areas'' => GIST.     * Pour ''synthese'' => SP-GIST, pour ''l_areas'' => GIST.
   * une table de relation (''cor_area_synthese'') plutôt qu'une recherche spatiale.   * une table de relation (''cor_area_synthese'') plutôt qu'une recherche spatiale.
     * ajout d'une colonne ''area_type_code'' sur ''cor_area_synthese''.     * ajout d'une colonne ''area_type_code'' sur ''cor_area_synthese''.
-  * st_intersects() sur des géométries complexes simplifiées via st_subdivide() +  * ''st_intersects()'' sur des géométries complexes simplifiées via ''st_subdivide()''. 
-  * une  table/VM de cache stockant le nombre d'observation par maille pour gérer l'affichage par défaut sans filtre. +  * une  table/VM de cache stockant le nombre d'observations par maille pour gérer l'affichage par défaut sans filtre. 
-    * sinon utiliser seulement la table ''cor_area_synthese'' (sans relation avec la table ''synthese'') pour les requêtes avec des tailles de mailles appropriées au niveau de zoom pour gérer l'affichage par défaut sans filtre+    * sinon utiliser seulement la table ''cor_area_synthese'' (sans relation avec la table ''synthese'') pour les requêtes avec des tailles de mailles appropriées au niveau de zoom pour gérer l'affichage par défaut sans filtre
 +  * des requêtes s'exécutant uniquement sur une seule table bien indexée sans jointure. 
 +  * des requêtes pour le module Synthese s'exécutant uniquement sur une vue matérialisée ''v_synthese_for_web_app'' bien indexée.
  
-==== Conclusion des tests vis à vis de l'utilisation des tuiles vecteurs ====+==== Conclusion sur l'utilisation des tuiles vecteurs ====
  
-Concernant l'amélioration des performances du module Synthese, les différents tests effectués nous ont permis de nous rendre compte que **l'utilisation des tuiles vecteurs n'était pas à elle seule une solution concluante**. A certain niveau de zoom, lorsque les bounding box des tuiles vecteurs se répartissent bien les mailles d’agrégation ou les observations affichées la parallélisation des requêtes effectuées en base est intéressante.\\ +Concernant l'amélioration des performances du module Synthese, les différents tests effectués nous ont permis de nous rendre compte que **l'utilisation des tuiles vecteurs n'était pas à elle seule une solution concluante**. A certains niveaux de zoom, lorsque les bounding box des tuiles vecteurs se répartissent bien les mailles d’agrégation ou les observations affichéesla parallélisation des requêtes effectuées en base est intéressante.\\ 
-Malheureusement, cette parallélisation des requêtes n'est pas valable à tous les niveaux de zoom. Avec les petits zoom, nous avons peu de requêtes parallélisées et elles s'exécutent sur de très grande surface et quantité de données. Les performances sont dégradées.+Malheureusement, cette parallélisation des requêtes n'est pas valable à tous les niveaux de zoom. Avec les petits zoom, nous avons peu de requêtes parallélisées et elles s'exécutent sur de très grandes surfaces et quantités de données. Les performances sont dégradées.
  
 En outre, pour que l'affichage sur la carte soit fluide et garantisse de bonnes conditions d'utilisation à l'utilisateur final, il est nécessaire que la création des  tuiles s'effectue très rapidement. À l'exception des requêtes exécutées sur une table mettant en cache le nombre d'observation par mailles, il n'a pas été possible de générer les tuiles assez vite pour garantir une impression de fluidité lors de l'affichage des tuiles sur la carte. En outre, pour que l'affichage sur la carte soit fluide et garantisse de bonnes conditions d'utilisation à l'utilisateur final, il est nécessaire que la création des  tuiles s'effectue très rapidement. À l'exception des requêtes exécutées sur une table mettant en cache le nombre d'observation par mailles, il n'a pas été possible de générer les tuiles assez vite pour garantir une impression de fluidité lors de l'affichage des tuiles sur la carte.
  
-==== Conclusion des tests vis à vis du framework MapLibre GL ====+==== Conclusion sur le framework MapLibre GL ====
  
-Le framework carto MapLibre GL a été facilement intégré à Angular lors de son utilisation dans le module Syntests. Une modification minime du cœur de GeoNature a toutefois été nécessaire. Nous avons pu constaté qu'il répondait bien à tout les besoins attendus d'une nouveau framework carto utilisable dans le cadre de GeoNature. Il apporte de nouvelles fonctionnalités très intéressantes : affichage du relief, rotation de la carte, traduction instantanée des libellées des cartes, changements de thèmes du rendu carto... C'est donc un choix très intéressant pour le remplacement des Leaflet.+Le framework carto MapLibre GL a été facilement intégré à Angular lors de son utilisation dans le module Syntests. Une modification minime du cœur de GeoNature a toutefois été nécessaire. Nous avons pu constaté qu'il répondait bien à tous les besoins attendus d'une nouveau framework cartographique utilisable dans le cadre de GeoNature. Il apporte de nouvelles fonctionnalités très intéressantes : affichage du relief, rotation de la carte, traduction instantanée des libellées des cartes, changements de thèmes du rendu cartographique... C'est donc un choix très intéressant pour le remplacement de Leaflet.
  
-Ceci dit, ce framework étant assez récent, il ne possède pas tous les plugins offerts par Leaflet. L'ajout des fonctionnalités d'édition de la carte s'est avéré ainsi plus complexe qu'avec Leaflet.+Cependant, ce framework étant assez récent, il ne possède pas tous les plugins offerts par Leaflet. L'ajout des fonctionnalités d'édition de la carte s'est avéré ainsi plus complexe qu'avec Leaflet
 + 
 +Au vu de la forte intégration de Leaflet aux composants génériques du frontend de GeoNature et au fait que l'utilisation des tuiles vecteurs ne résout pas les problèmes de performance, la bascule vers ce nouveau framework ne semble pas nécessaire. Il semble donc plus pertinent de continuer à utiliser Lealflet avec un rendu des observations à l'aide de GeoJson.
  
-Au vue de la forte intégration de Leaflet aux composant générique du frontend de GeoNature et au fait que l'utilisation des tuiles vecteurs ne résolvent pas les problèmes de performance, la bascule vers ce nouveau framework ne semble pas nécessaire. Il semble donc plus pertinent de continuer à utiliser Lealflet avec un rendu des observations à l'aide de GeoJson. 
 ===== Conclusion générale ===== ===== Conclusion générale =====
  
-===== Ressources Web ===== +Nous proposons d'améliorer les performances du module Synthese en : 
-  * Module externe de test : [[https://github.com/cbn-alpin/gn_module_syntests|Syntests]] +  * séparant les web services des principales fonctionnalités du module (carte, liste paginée, export)
-  * Listes des tickets GeoNature traitant du sujet : +  * créant un web service paginé côté serveur pour la liste des observations affichées sous forme de tableau 
-    * [[https://github.com/PnX-SI/GeoNature/issues/1093| #1093 - Synthèse : affichage de mailles et observations géo-confidentielles]] +  s'appuyant sur les mécanismes utilisés dans le module Export (limitation de l'utilisation mémoire), une génération de l'export exécuté en arrière plan (tâche Celery) et l'utilisation des notifications à l'utilisateur pour les téléchargements 
-    [[https://github.com/PnX-SI/GeoNature/issues/560| #560 - Synthèse - Travail sur les performances]] +  * utilisant une seule VM bien indexée pour exécuter toutes les requêtes SQL de la Synthese 
-    * [[https://github.com/PnX-SI/GeoNature/issues/559| #559 - Synthese: ajout plugin Leaflet clusterMarker]]  +  maintenant l'utilisation de Leaflet et du GeoJson 
-    * [[https://github.com/PnX-SI/GeoNature/issues/728| #728 - CARTO - permettre l'affichage de plusieurs couches simultanément]] +  * vérifiant systématiquement les performances des requêtes à l'aide de ''EXPLAIN'' et son [[https://explain.dalibo.com/outil de visualisation]] ou d'outils de statistiques comme ''pg_stat_statements'' 
-  * Sources de données : +  en basculant automatiquement sur une agrégation par mailles des observations en fonction du nombre d'observation à afficher sur la carte
-    [[https://openmaptiles.org/about/| Ressources proposées par OpenMapTiles]] +
-    * [[https://guides.data.gouv.fr/reutiliser-des-donnees/utiliser-les-api-geographiques/utiliser-les-tuiles-vectorielles| Présentation des tuiles vecteurs proposée par Etalab]] +
-    * [[https://geoservices.ign.fr/documentation/services/api-et-services-ogc/tuiles-vectorielles-tmswmts/styles| Tuiles et styles proposés par IGN]] +
-  * Amélioration des index spatiaux : +
-    * [[https://www.crunchydata.com/blog/the-many-spatial-indexes-of-postgis| (The Many) Spatial Indexes of PostGIS]] : Paul Ramsey, 5 mai 2021. +
-    * [[https://www.linkedin.com/advice/0/what-best-practices-simplifying-geometries-reducing-attributes| Quelles sont les meilleures pratiques pour simplifier les géométries et réduire les attributs dans PostGIS pour les tuiles vectorielles ?]] : IA et communauté Linkedin. +
-    * [[https://www.alibabacloud.com/blog/postgresql-best-practices-selection-and-optimization-of-postgis-spatial-indexes-gist-brin-and-r-tree_597034PostgreSQL Best Practices: Selection and Optimization of PostGIS Spatial Indexes (GiST, BRIN, and R-tree)]] : Digoal, 18 décembre 2020. +
-    [[https://symphony.is/about-us/blog/boosting-postgis-performance| Boosting PostGIS Performance]] : Symphony, 7 mars 2022. +
-    * [[https://blog.cleverelephant.ca/2021/05/indexes-and-queries.html| Spatial Indexes and Bad Queries]] : Paul Ramsey, 4 mai 2021. +
-    * [[https://blog.cleverelephant.ca/2018/09/postgis-external-storage.html| 5x Faster Spatial Join with this One Weird Trick]] : Paul Ramsey, 28 septembre 2018. +
  
-==== Analyse du nombres d'observation max par communes et taxons ==== 
-Requêtes Communes : 
-<code sql> 
-SELECT  
- a.area_name,  
- a.area_code, 
- COUNT(s.id_synthese) AS obs_nbr 
-FROM gn_synthese.synthese AS s 
- JOIN gn_synthese.cor_area_synthese AS cas 
- ON (cas.id_synthese = s.id_synthese AND cas.area_type_code = 'COM') 
- JOIN ref_geo.l_areas AS a  
- ON cas.id_area = a.id_area  
-GROUP BY a.area_name, a.area_code  
-ORDER BY obs_nbr DESC ; 
  
-SELECT  
- a.area_name,  
- a.area_code, 
- COUNT(s.id_synthese) AS obs_nbr 
-FROM gn_synthese.synthese AS s 
- JOIN gn_synthese.cor_area_synthese AS cas 
- ON cas.id_synthese = s.id_synthese  
- JOIN ref_geo.l_areas AS a  
- ON (cas.id_area = a.id_area AND a.id_type = ref_geo.get_id_area_type_by_code('COM')) 
-GROUP BY a.area_name, a.area_code  
-ORDER BY obs_nbr DESC ; 
-</code> 
-Résultats [[projets:silene-geonature:test-amelioration-performance-synthese:obs-nbr-communes|pour les Communes]] sur la base SINP AURA de 23 millions de données du 2024-05-20 :  
-  * max d'observation pour une commune : 245 043 
  
-Requêtes Taxons : 
-<code sql> 
-SELECT  
- t2.cd_nom AS sciname_code, 
- t2.lb_nom AS sciname,  
- t2.nom_vern AS vernaname, 
- COUNT(s.id_synthese) AS obs_nbr 
-FROM gn_synthese.synthese AS s 
- JOIN taxonomie.taxref AS t1 
- ON t1.cd_nom = s.cd_nom  
- JOIN taxonomie.taxref AS t2  
- ON t2.cd_nom = t1.cd_ref  
-GROUP BY t2.lb_nom, t2.cd_nom 
-ORDER BY obs_nbr DESC  
-LIMIT 2000 ; 
-</code> 
-Résultats [[projets:silene-geonature:test-amelioration-performance-synthese:obs-nbr-taxons|pour les Taxons]] sur la base SINP AURA de 23 millions de données du 2024-05-20 :  
-  * taxon avec le max d'observation : 613 465 obs 
-  * taxons avec plus de 150 000 obs : 25 / 32 465 taxons 
- 
-==== Utilisation de mailles 20km et 50km ==== 
-  * Source des mailles :  https://inpn.mnhn.fr/telechargement/cartes-et-information-geographique 
-  * Procédure :  
-    * Télécharger manuellement [[https://inpn.mnhn.fr/telechargement/cartes-et-information-geographique| les archives Zip]] 
-    * Extraire manuellement les archives 
-    * Utiliser [[https://postgis.net/docs/using_postgis_dbmanagement.html#shp2pgsql_usage| Shp2pgsql]] :  
-<color #ed1c24>Note : le paquet postgis doit être isntallé pour pouvoir utiliser Shp2pgsql.</color> 
-      * M20 : ''shp2pgsql METROP_L9320X20.shp ref_geo.m20 | psql -h localhost -p 5432 -U geonatadmin -d gn2_default'' 
-      * M50 : ''shp2pgsql METROP_L9350X50.shp ref_geo.m50 | psql -h localhost -p 5432 -U geonatadmin -d gn2_default'' 
-    * Insérer les mailles M20 :<code sql> 
-INSERT INTO ref_geo.bib_areas_types ( 
- type_name, 
- type_code, 
- type_desc, 
- ref_name, 
- ref_version, 
- num_version, 
- size_hierarchy 
-) VALUES ( 
- 'Mailles20*20', 
- 'M20', 
- 'Type maille INPN 20*20km', 
- NULL, 
- 16082021, 
- NULL, 
- 20000 
-); 
- 
-INSERT INTO ref_geo.l_areas ( 
- id_type, 
- area_name, 
- area_code, 
- geom, 
- geom_4326, 
- centroid, 
- "source", 
- "comment", 
- "enable", 
- additional_data 
-) 
- SELECT DISTINCT 
- ref_geo.get_id_area_type('M20'), 
- m.cd_sig, 
- m.code_20km, 
- m.geom, 
- st_transform(ST_SetSRID(m.geom, 2154), 4326), 
- st_centroid(m.geom), 
- 'INPN', 
- NULL, 
- TRUE, 
- NULL 
- FROM ref_geo.m20 AS m  
- JOIN ref_geo.tmp_subdivided_areas AS a  
- ON (m.geom && a.geom AND a.type_code = 'SINP') ; 
- 
-CREATE TABLE gn_synthese.cor_area_synthese_m20 PARTITION OF gn_synthese.cor_area_synthese_partitioned 
-     FOR VALUES IN ('M20'); 
-</code> 
-    * Insérer les mailles M50 :<code sql> 
-INSERT INTO ref_geo.bib_areas_types ( 
- type_name, 
- type_code, 
- type_desc, 
- ref_name, 
- ref_version, 
- num_version, 
- size_hierarchy 
-) VALUES ( 
- 'Mailles50*50', 
- 'M50', 
- 'Type maille INPN 50*50km', 
- NULL, 
- 16082021, 
- NULL, 
- 50000 
-); 
- 
- 
-INSERT INTO ref_geo.l_areas ( 
- id_type, 
- area_name, 
- area_code, 
- geom, 
- geom_4326, 
- centroid, 
- "source", 
- "comment", 
- "enable", 
- additional_data 
-) 
- SELECT DISTINCT 
- ref_geo.get_id_area_type('M50'), 
- m.cd_sig, 
- m.code_50km, 
- m.geom, 
- st_transform(ST_SetSRID(m.geom, 2154), 4326), 
- st_centroid(m.geom), 
- 'INPN', 
- NULL, 
- TRUE, 
- NULL 
- FROM ref_geo.m50 AS m  
-            JOIN ref_geo.tmp_subdivided_areas AS a  
-                ON (m.geom && a.geom AND a.type_code = 'SINP') ;  
- 
-CREATE TABLE gn_synthese.cor_area_synthese_m50 PARTITION OF gn_synthese.cor_area_synthese_partitioned 
-     FOR VALUES IN ('M50'); 
-</code> 
-  * Si besoins mettre à jour le champ ''area_type_code'' : <code sql> 
-WITH m20 AS ( 
- SELECT id_area  
- FROM ref_geo.l_areas  
- WHERE id_type = ref_geo.get_id_area_type('M20') 
-) 
-UPDATE gn_synthese.cor_area_synthese AS cas SET 
- area_type_code = 'M20' 
-FROM m20 
-WHERE cas.id_area = m20.id_area; 
- 
-WITH m50 AS ( 
- SELECT id_area  
- FROM ref_geo.l_areas  
- WHERE id_type = ref_geo.get_id_area_type('M50') 
-) 
-UPDATE gn_synthese.cor_area_synthese AS cas SET 
- area_type_code = 'M50' 
-FROM m50 
-WHERE cas.id_area = m50.id_area; 
-</code> 
-  * Activer seulement les mailles M20 de la région SINP : <code sql> 
-UPDATE ref_geo.l_areas AS a 
- SET "enable" = FALSE 
-WHERE a.id_type = ref_geo.get_id_area_type('M20'); 
- 
-UPDATE ref_geo.l_areas AS a 
- SET "enable" = TRUE 
-FROM ref_geo.tmp_subdivided_areas AS sa  
-WHERE a.id_type = ref_geo.get_id_area_type('M20' 
- AND a.geom && sa.geom  
- AND sa.type_code = 'SINP';  
-</code> 
-  * Activer seulement les mailles M50 de la région SINP :<code sql> 
-UPDATE ref_geo.l_areas AS a 
- SET "enable" = FALSE 
-WHERE a.id_type = ref_geo.get_id_area_type('M50'); 
- 
-UPDATE ref_geo.l_areas AS a 
- SET "enable" = TRUE 
-FROM ref_geo.tmp_subdivided_areas AS sa  
-WHERE a.id_type = ref_geo.get_id_area_type('M50' 
- AND a.geom && sa.geom  
- AND sa.type_code = 'SINP'; 
-</code> 
-===== Tests d'outils ===== 
- 
-==== Utilisation de pg_stat_statements ==== 
-  * Ressource : [[https://docs.postgresql.fr/15/pgstatstatements.html| Doc Postgresql 15 "pg_stat_statements", en français]] 
-  * ''pg_stat_statements'' est une extension Postgresql qui permet d'avoir un historique des requêtes lancés, leur fréquence, durée... 
-    * Étant données que les index spécialisées doivent être créé en fonction des requêtes les plus fréquentes, cet outil va nous servir à les identifiés et suivre leurs évolutions. 
-  * Procédure de mise en place : 
-    * Définir les paramètres de config Postgresql suivant : <code> 
-shared_preload_libraries = 'pg_stat_statements' 
-compute_query_id = on 
-pg_stat_statements.max = 10000 
-pg_stat_statements.track = all 
-</code> 
-    * Ajouter l'extension suivante à la base de données à surveiller : <code sql>CREATE EXTENSION pg_stat_statements ;</code> 
-    * Vous pourrez ensuite consultez les requêtes exécutés avec : <code sql> 
-SELECT pd.datname, pr.rolname, pss.query, pss.calls, (pss.mean_exec_time/1000) AS sec_mean_exec_time 
-FROM pg_stat_statements AS pss 
- JOIN pg_roles AS pr ON (pr."oid" = pss.userid) 
- JOIN pg_catalog.pg_database AS pd ON (pd."oid" = pss.dbid) 
-WHERE pr.rolname = 'geonatadmin' 
- AND pd.datname = 'gn2_default'  
- AND pss.mean_exec_time > 500 
-ORDER BY pss.calls DESC, pss.mean_exec_time DESC; 
-</code> 
-  * ''pg_stat_statements'' permet de connaître les requêtes les plus fréquentes, ce qui permet ensuite de les analyser avec ''EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)'' et [[l'outil en ligne Explain de Dalibo| https://explain.dalibo.com/]]. 
-  * Voir aussi [[https://www.crunchydata.com/blog/cleaning-up-your-postgres-database| cet article pour identifiez les index inutilisés]]. 
- 
-==== Test du serveur de fond carto : Tileserver-GL === 
-  * Utiliser Docker pour installer le TileServer GL. Debian ne possède pas nativement des paquets pour toutes les dépendances. 
- 
-==== Debugger de tuiles vecteurs ==== 
-  * [[https://chromewebstore.google.com/detail/mfikcokdfehaofebfhoehbajfgbofdpk| Extension Chrome Mapbox Vector Tile]] : ajoute un onglet dans les outils développeur]] 
-  * [[https://stevage.github.io/vector-inspector/| Vector Inspector]] 
-  * [[https://openlayers.org/en/latest/examples/canvas-tiles.html| Affichage z,x,y des tuiles - OpenLayers]] 
-  * [[https://onthegomap.github.io/planetiler-demo/| Affichage z,x,y des tuiles - Maplibre Planetiler]] 
- 
-==== Test framework carto web Maplibre ==== 
-  * Création des outils de base de visualisation de la carte  très facile : zoom, passage en 3D, geolocalisation,  
-  * Liste des plugins Maplibre GL : https://maplibre.org/maplibre-gl-js/docs/plugins/ 
-    * Plugin d'outils d'édition //Mapbox-Gl-Draw// : https://github.com/mapbox/mapbox-gl-draw 
-      * Comment ajouter un mode (fonctionnalité d'édition) :  
-      * Commenta ajouter une icone d'outil : 
-      * Mode édition de rectangle : 
-      * Mode édition de cercle :  
-    * Plugin d'ajout de légende : https://github.com/watergis/mapbox-gl-legend 
-    * Plugin de gestion de couches et de leur opacité : https://github.com/mug-jp/maplibre-gl-opacity 
-    * Plugin tuiles GeoJson : https://github.com/mkeller3/mapbox-gl-ogc-feature-collection 
-    * Plugin d'inspection des features (debug) : https://github.com/maplibre/maplibre-gl-inspect 
-  * Intégration de Mapblibre avec Angular "@maplibre/ngx-maplibre-gl" : https://maplibre.org/ngx-maplibre-gl/demo/display-map 
-    * Documentation API Angular "@maplibre/ngx-maplibre-gl" : https://maplibre.org/ngx-maplibre-gl/doc 
-    * Code source des exemples Maplibre et Angular : https://github.com/maplibre/ngx-maplibre-gl/tree/c04efff199c1d21016c9bd2d6ba774286c5c3e77/projects/showcase/src/app/demo/examples 
- 
-==== Test d'utilisation de mailles hexagonales ==== 
-  * Ressources :  
-    * [[https://www.crunchydata.com/blog/tile-serving-with-dynamic-geometry| Tile Serving with Dynamic Geometry]] : Paul Ramsey, 24 mars 2020. 
-    * [[https://medium.com/geolytix/fishnets-honeycombs-and-footballs-better-spatial-models-with-hexagonal-grids-768bdf92d3bb| Fishnets, Honeycombs and Footballs; Better spatial models with hexagonal grids]] : Dennis Bauszus, 3 mars 2017. 
-    * [[https://medium.com/geolytix/hex-grid-algorithm-for-postgis-4ac45f61d093| Hex grid algorithm for PostGIS]] : Dennis Bauszus, 3 novembre 2017. 
- 
-Code utilisé : 
-<code sql> 
-CREATE OR REPLACE 
-FUNCTION 
-public.hexagon(i integer, j integer, edge float8) 
-RETURNS geometry 
-AS $$ 
-DECLARE 
-h float8 := edge*cos(pi()/6.0); 
-cx float8 := 1.5*i*edge; 
-cy float8 := h*(2*j+abs(i%2)); 
-BEGIN 
-RETURN ST_MakePolygon(ST_MakeLine(ARRAY[ 
-            ST_MakePoint(cx - 1.0*edge, cy + 0), 
-            ST_MakePoint(cx - 0.5*edge, cy + -1*h), 
-            ST_MakePoint(cx + 0.5*edge, cy + -1*h), 
-            ST_MakePoint(cx + 1.0*edge, cy + 0), 
-            ST_MakePoint(cx + 0.5*edge, cy + h), 
-            ST_MakePoint(cx - 0.5*edge, cy + h), 
-            ST_MakePoint(cx - 1.0*edge, cy + 0) 
-         ])); 
-END; 
-$$ 
-LANGUAGE 'plpgsql' 
-IMMUTABLE 
-STRICT 
-PARALLEL SAFE; 
- 
-CREATE OR REPLACE 
-FUNCTION public.hexagonCoordinates(bounds geometry, edge float8, 
-                            OUT i integer, OUT j integer) 
-RETURNS SETOF record 
-AS $$ 
-    DECLARE 
-        h float8 := edge*cos(pi()/6); 
-        mini integer := floor(st_xmin(bounds) / (1.5*edge)); 
-        minj integer := floor(st_ymin(bounds) / (2*h)); 
-        maxi integer := ceil(st_xmax(bounds) / (1.5*edge)); 
-        maxj integer := ceil(st_ymax(bounds) / (2*h)); 
-    BEGIN 
-    FOR i, j IN 
-    SELECT a, b 
-    FROM generate_series(mini, maxi) a, 
-         generate_series(minj, maxj) b 
-    LOOP 
-       RETURN NEXT; 
-    END LOOP; 
-    END; 
-$$ 
-LANGUAGE 'plpgsql' 
-IMMUTABLE 
-STRICT 
-PARALLEL SAFE; 
- 
-CREATE OR REPLACE 
-FUNCTION public.tileHexagons(z integer, x integer, y integer, step integer, 
-                      OUT geom geometry(Polygon, 3857), OUT i integer, OUT j integer) 
-RETURNS SETOF record 
-AS $$ 
-    DECLARE 
-        bounds geometry; 
-        maxbounds geometry := ST_TileEnvelope(0, 0, 0); 
-        edge float8; 
-    BEGIN 
-    bounds := ST_TileEnvelope(z, x, y); 
-    edge := (ST_XMax(bounds) - ST_XMin(bounds)) / pow(2, step); 
-    FOR geom, i, j IN 
-    SELECT ST_SetSRID(hexagon(h.i, h.j, edge), 3857), h.i, h.j 
-    FROM hexagoncoordinates(bounds, edge) h 
-    LOOP 
-       IF maxbounds ~ geom AND bounds && geom THEN 
-            RETURN NEXT; 
-         END IF; 
-     END LOOP; 
-     END; 
-$$ 
-LANGUAGE 'plpgsql' 
-IMMUTABLE 
-STRICT 
-PARALLEL SAFE; 
- 
-CREATE OR REPLACE 
-FUNCTION public.hexagons(z integer, x integer, y integer, step integer default 4) 
-RETURNS bytea 
-AS $$ 
-WITH 
-bounds AS ( 
-    -- Convert tile coordinates to web mercator tile bounds 
-    SELECT ST_TileEnvelope(z, x, y) AS geom 
- ), 
- rows AS ( 
-    -- All the hexes that interact with this tile 
-    SELECT h.i, h.j, h.geom 
-    FROM TileHexagons(z, x, y, step) h 
- ), 
- mvt AS ( 
-     -- Usual tile processing, ST_AsMVTGeom simplifies, quantizes, 
-     -- and clips to tile boundary 
-    SELECT ST_AsMVTGeom(rows.geom, bounds.geom) AS geom, 
-           rows.i, rows.j 
-    FROM rows, bounds 
-) 
--- Generate MVT encoding of final input record 
-SELECT ST_AsMVT(mvt, 'hexagons') FROM mvt 
-$$ 
-LANGUAGE 'sql' 
-STABLE 
-STRICT 
-PARALLEL SAFE; 
- 
-COMMENT ON FUNCTION public.hexagons IS 'Hex coverage dynamically generated. Step parameter determines how approximately many hexes (2^step) to generate per tile.'; 
- 
--- Test de requête 
--- 10, 523, 369 
-WITH bounds AS ( 
-    -- Convert tile coordinates to web mercator tile bounds 
-    SELECT ST_TileEnvelope(11, 1046, 739) AS geom 
-), 
-rows AS ( 
-    -- All the hexes that interact with this tile 
-    SELECT 
-        h.i, 
-        h.j, 
-        h.geom, 
-        st_transform(h.geom, 4326) AS geom_4326 
-    FROM tileHexagons(9, 261, 184, 4) AS h 
- ), 
-observations AS ( 
-    SELECT 
-        r.geom, 
-        r.i, 
-        r.j, 
-        COUNT(s.id_synthese) AS nbr 
-    FROM gn_synthese.synthese AS s 
-        JOIN "rows" AS r 
-            ON st_intersects(s.the_geom_4326, r.geom_4326) 
-    GROUP BY r.geom, r.i, r.j 
-) 
--- Usual tile processing, ST_AsMVTGeom simplifies, quantizes, 
- -- and clips to tile boundary 
-SELECT 
-    ST_AsMVTGeom(o.geom, b.geom) AS geom, 
-    o.i, 
-    o.j, 
-    o.nbr 
-FROM observations AS o, bounds AS b  
-WHERE ST_AsMVTGeom(o.geom, b.geom) IS NOT NULL; 
-</code> 
- 
- 
-**Résultats** : l'agrégation d'observations sur des mailles hexagonales générées dynamiquement est assez rapide aux niveaux de zoom 10 et plus.  
- 
-==== Tests de requête SQL d'aggrégation ==== 
-<code sql> 
- 
- 
-SELECT  
-   t.id_area, 
-   t.area_code, 
-   t.area_name, 
-   t.geom 
-FROM ref_geo.l_areas AS t 
-WHERE ST_Intersects(t.geom, ST_Transform(ST_TileEnvelope(12, 2117, 1479), 2154)) 
- AND t.id_type = ref_geo.get_id_area_type('COM'); 
- 
-SELECT public.squares(12, 2117, 1479); 
- 
--- Communes 
-SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 9 row(s) fetched - 0,010s (0,001s fetch), on 2024-03-19 at 16:32:36 
-SELECT ST_TileEnvelope(9, 264, 184) AS envelope; -- 155 row(s) fetched - 0,073s (0,019s fetch), on 2024-03-19 at 16:32:17 
-SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 408 row(s) fetched - 0,188s (0,071s fetch), on 2024-03-19 at 16:36:21 
-SELECT ST_TileEnvelope(7, 66, 46) AS envelope; -- 873 row(s) fetched - 0,366s (0,097s fetch), on 2024-03-19 at 16:35:29 
-SELECT ST_TileEnvelope(6, 33, 23) AS envelope; -- 1311 row(s) fetched - 0,490s (0,113s fetch), on 2024-03-19 at 16:37:22 
-SELECT ST_TileEnvelope(6, 32, 23) AS envelope; -- 6763 row(s) fetched - 1s (0,439s fetch), on 2024-03-19 at 16:37:55 
-SELECT ST_TileEnvelope(5, 16, 11) AS envelope; -- 24849 row(s) fetched - 4s (0,924s fetch), on 2024-03-19 at 16:31:44 
-SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 30521 row(s) fetched - 4s (0,690s fetch), on 2024-03-19 at 16:34:23 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(8, 132, 92) AS envelope 
-) 
-SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom,  
-       t.id_area, 
-       t.area_code, 
-       t.area_name 
-FROM ref_geo.l_areas AS t, bounds AS b 
-WHERE t.geom && ST_Transform(b.envelope, 2154) 
- AND t.id_type = ref_geo.get_id_area_type('COM') ; 
- 
--- Maille M1 
-SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 72 row(s) fetched - 0,003s (0,001s fetch), on 2024-03-19 at 16:57:41 
-SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 13161 row(s) fetched - 0,211s (0,079s fetch), on 2024-03-19 at 16:59:17 
-SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 481502 row(s) fetched - 6s (3s fetch), on 2024-03-19 at 17:00:30 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(4, 8, 5) AS envelope 
-) 
-SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom,  
-       t.id_area, 
-       t.area_code, 
-       t.area_name 
-FROM ref_geo.l_areas AS t, bounds AS b 
-WHERE t.geom && ST_Transform(b.envelope, 2154) 
- AND t.id_type = ref_geo.get_id_area_type('M1') ; 
- 
- 
--- Maille M5 
-SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 6 row(s) fetched - 0,005s (0,001s fetch), on 2024-03-19 at 16:58:06 
-SELECT ST_TileEnvelope(8, 132, 92) AS envelope; --  533 row(s) fetched - 0,021s (0,006s fetch), on 2024-03-19 at 16:58:32 
-SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 18856 row(s) fetched - 0,329s (0,119s fetch), on 2024-03-19 at 17:01:26 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(4, 8, 5) AS envelope 
-) 
-SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom,  
-       t.id_area, 
-       t.area_code, 
-       t.area_name 
-FROM ref_geo.l_areas AS t, bounds AS b 
-WHERE t.geom && ST_Transform(b.envelope, 2154) 
- AND t.id_type = ref_geo.get_id_area_type('M5') ; 
- 
--- Maille M10 
-SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 2 row(s) fetched - 0,002s, on 2024-03-19 at 17:03:21 
-SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 156 row(s) fetched - 0,005s (0,001s fetch), on 2024-03-19 at 17:02:58 
-SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 6013 row(s) fetched - 0,088s (0,036s fetch), on 2024-03-19 at 17:02:07 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope 
-) 
-SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom,  
-       t.id_area, 
-       t.area_code, 
-       t.area_name 
-FROM ref_geo.l_areas AS t, bounds AS b 
-WHERE t.geom && ST_Transform(b.envelope, 2154) 
- AND t.id_type = ref_geo.get_id_area_type('M10') ; 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope 
-), 
-observations AS ( 
- SELECT  
- l.area_code AS code, 
- ST_Transform(l.geom, 3857) AS geom, 
- COUNT(s.id_synthese) AS nbr 
- FROM gn_synthese.synthese AS s  
- JOIN gn_synthese.cor_area_synthese AS cas  
- ON s.id_synthese = cas.id_synthese  
- JOIN ref_geo.l_areas AS l 
- ON l.id_area = cas.id_area  
- , bounds AS b 
- WHERE l.geom && ST_Transform(b.envelope, 2154) 
- AND l.id_type = ref_geo.get_id_area_type('M10') 
- GROUP BY l.area_code, l.geom 
-) 
-SELECT ST_AsMVTGeom(o.geom, b.envelope) AS geom,  
-       o.nbr, 
-       o.code  
-FROM observations AS o, bounds AS b ; 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(9, 264, 184) AS envelope 
-) 
-SELECT  
- l.area_code AS code, 
- ST_Transform(l.geom, 3857) AS geom, 
- COUNT(s.id_synthese) AS nbr 
-FROM gn_synthese.synthese AS s  
- JOIN gn_synthese.cor_area_synthese AS cas  
- ON s.id_synthese = cas.id_synthese  
- JOIN ref_geo.l_areas AS l 
- ON l.id_area = cas.id_area  
- , bounds AS b 
-WHERE l.geom && ST_Transform(b.envelope, 2154) 
- AND l.id_type = ref_geo.get_id_area_type('M10') 
-GROUP BY l.area_code, l.geom ; -- 32 row(s) fetched - 7s (0,001s fetch), on 2024-03-19 at 17:20:06 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(9, 264, 184) AS envelope 
-) 
-SELECT l.id_area, l.geom, l.area_code 
-FROM ref_geo.l_areas AS l, bounds AS b 
-WHERE l.geom && ST_Transform(b.envelope, 2154) 
- AND l.id_type = ref_geo.get_id_area_type('M10') ; -- 32 row(s) fetched - 0,007s (0,001s fetch), on 2024-03-19 at 17:29:37 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(9, 264, 184) AS envelope 
-), 
-m10 AS ( 
- SELECT  
- l.id_area,  
- l.geom,  
- l.area_code AS code 
- FROM ref_geo.l_areas AS l, bounds AS b 
- WHERE l.geom && ST_Transform(b.envelope, 2154) 
- AND l.id_type = ref_geo.get_id_area_type('M10') 
-) 
-SELECT  
- m10.code, 
- ST_Transform(m10.geom, 3857) AS geom, 
- COUNT(s.id_synthese) AS nbr 
-FROM gn_synthese.synthese AS s 
- JOIN gn_synthese.cor_area_synthese AS cas  
- ON s.id_synthese = cas.id_synthese 
- JOIN m10  
- ON cas.id_area = m10.id_area 
-GROUP BY m10.code, m10.geom ; -- 32 row(s) fetched - 7s, on 2024-03-19 at 17:39:22 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(9, 264, 184) AS envelope 
-), 
-m10 AS ( 
- SELECT l.id_area, l.geom, l.area_code AS code 
- FROM ref_geo.l_areas AS l, bounds AS b 
- WHERE l.geom && ST_Transform(b.envelope, 2154) 
- AND l.id_type = ref_geo.get_id_area_type('M10') 
-) 
-SELECT  
- m10.code, 
- ST_Transform(m10.geom, 3857) AS geom, 
- COUNT(s.id_synthese) AS nbr 
-FROM gn_synthese.synthese AS s 
- JOIN m10  
- ON s.the_geom_local && m10.geom 
-GROUP BY m10.code, m10.geom ; -- 32 row(s) fetched - 27s (0,001s fetch), on 2024-03-19 at 17:35:57 
- 
- 
-WITH bounds AS ( 
- SELECT ST_TileEnvelope(8, 132, 92) AS envelope 
-), mvtgeom AS ( 
- SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom,  
-        t.id_area, 
-        t.area_code, 
-        t.area_name 
- FROM ref_geo.l_areas AS t, bounds AS b 
- WHERE ST_Intersects(t.geom, ST_Transform(b.envelope, 2154)) 
- AND t.id_type = ref_geo.get_id_area_type('COM') 
-) 
-SELECT ST_AsMVT(mvtgeom.*) FROM mvtgeom ; 
-</code> 
  
 ===== Annexes ===== ===== Annexes =====
Ligne 2266: Ligne 1674:
 CREATE INDEX idx_sfwa_id_nomenclature_valid_status ON gn_synthese.v_synthese_for_web_app_full USING btree (id_nomenclature_valid_status); CREATE INDEX idx_sfwa_id_nomenclature_valid_status ON gn_synthese.v_synthese_for_web_app_full USING btree (id_nomenclature_valid_status);
 CREATE INDEX idx_sfwaf2_geojson ON gn_synthese.v_synthese_for_web_app_full USING gin ("st_asgeojson"); CREATE INDEX idx_sfwaf2_geojson ON gn_synthese.v_synthese_for_web_app_full USING gin ("st_asgeojson");
 +</code>
 +
 +==== Annexe 20 - Ressources Web ====
 +  * Module externe de test : [[https://github.com/cbn-alpin/gn_module_syntests|Syntests]]
 +  * Listes des tickets GeoNature traitant du sujet :
 +    * [[https://github.com/PnX-SI/GeoNature/issues/1093| #1093 - Synthèse : affichage de mailles et observations géo-confidentielles]]
 +    * [[https://github.com/PnX-SI/GeoNature/issues/560| #560 - Synthèse - Travail sur les performances]]
 +    * [[https://github.com/PnX-SI/GeoNature/issues/559| #559 - Synthese: ajout plugin Leaflet clusterMarker]] 
 +    * [[https://github.com/PnX-SI/GeoNature/issues/728| #728 - CARTO - permettre l'affichage de plusieurs couches simultanément]]
 +  * Sources de données :
 +    * [[https://openmaptiles.org/about/| Ressources proposées par OpenMapTiles]]
 +    * [[https://guides.data.gouv.fr/reutiliser-des-donnees/utiliser-les-api-geographiques/utiliser-les-tuiles-vectorielles| Présentation des tuiles vecteurs proposée par Etalab]]
 +    * [[https://geoservices.ign.fr/documentation/services/api-et-services-ogc/tuiles-vectorielles-tmswmts/styles| Tuiles et styles proposés par IGN]]
 +  * Amélioration des index spatiaux :
 +    * [[https://www.crunchydata.com/blog/the-many-spatial-indexes-of-postgis| (The Many) Spatial Indexes of PostGIS]] : Paul Ramsey, 5 mai 2021.
 +    * [[https://www.linkedin.com/advice/0/what-best-practices-simplifying-geometries-reducing-attributes| Quelles sont les meilleures pratiques pour simplifier les géométries et réduire les attributs dans PostGIS pour les tuiles vectorielles ?]] : IA et communauté Linkedin.
 +    * [[https://www.alibabacloud.com/blog/postgresql-best-practices-selection-and-optimization-of-postgis-spatial-indexes-gist-brin-and-r-tree_597034| PostgreSQL Best Practices: Selection and Optimization of PostGIS Spatial Indexes (GiST, BRIN, and R-tree)]] : Digoal, 18 décembre 2020.
 +    * [[https://symphony.is/about-us/blog/boosting-postgis-performance| Boosting PostGIS Performance]] : Symphony, 7 mars 2022.
 +    * [[https://blog.cleverelephant.ca/2021/05/indexes-and-queries.html| Spatial Indexes and Bad Queries]] : Paul Ramsey, 4 mai 2021.
 +    * [[https://blog.cleverelephant.ca/2018/09/postgis-external-storage.html| 5x Faster Spatial Join with this One Weird Trick]] : Paul Ramsey, 28 septembre 2018. 
 +
 +==== Annexe 21 - Analyse du nombres d'observation max par communes et taxons ====
 +Requêtes Communes :
 +<code sql>
 +SELECT 
 + a.area_name, 
 + a.area_code,
 + COUNT(s.id_synthese) AS obs_nbr
 +FROM gn_synthese.synthese AS s
 + JOIN gn_synthese.cor_area_synthese AS cas
 + ON (cas.id_synthese = s.id_synthese AND cas.area_type_code = 'COM')
 + JOIN ref_geo.l_areas AS a 
 + ON cas.id_area = a.id_area
 +GROUP BY a.area_name, a.area_code 
 +ORDER BY obs_nbr DESC ;
 +
 +SELECT 
 + a.area_name, 
 + a.area_code,
 + COUNT(s.id_synthese) AS obs_nbr
 +FROM gn_synthese.synthese AS s
 + JOIN gn_synthese.cor_area_synthese AS cas
 + ON cas.id_synthese = s.id_synthese 
 + JOIN ref_geo.l_areas AS a 
 + ON (cas.id_area = a.id_area AND a.id_type = ref_geo.get_id_area_type_by_code('COM'))
 +GROUP BY a.area_name, a.area_code 
 +ORDER BY obs_nbr DESC ;
 +</code>
 +Résultats [[fonctionnalites:geonature:synthese-amelioration-performance-test:obs-nbr-communes|pour les Communes]] sur la base SINP AURA de 23 millions de données du 2024-05-20 : 
 +  * max d'observation pour une commune : 245 043
 +
 +Requêtes Taxons :
 +<code sql>
 +SELECT 
 + t2.cd_nom AS sciname_code,
 + t2.lb_nom AS sciname, 
 + t2.nom_vern AS vernaname,
 + COUNT(s.id_synthese) AS obs_nbr
 +FROM gn_synthese.synthese AS s
 + JOIN taxonomie.taxref AS t1
 + ON t1.cd_nom = s.cd_nom 
 + JOIN taxonomie.taxref AS t2 
 + ON t2.cd_nom = t1.cd_ref
 +GROUP BY t2.lb_nom, t2.cd_nom
 +ORDER BY obs_nbr DESC 
 +LIMIT 2000 ;
 +</code>
 +Résultats [[fonctionnalites:geonature:synthese-amelioration-performance-test:obs-nbr-taxons|pour les Taxons]] sur la base SINP AURA de 23 millions de données du 2024-05-20 : 
 +  * taxon avec le max d'observation : 613 465 obs
 +  * taxons avec plus de 150 000 obs : 25 / 32 465 taxons
 +
 +==== Annexe 22 - Test utilisation de mailles 20km et 50km ====
 +  * Source des mailles :  https://inpn.mnhn.fr/telechargement/cartes-et-information-geographique
 +  * Procédure : 
 +    * Télécharger manuellement [[https://inpn.mnhn.fr/telechargement/cartes-et-information-geographique| les archives Zip]]
 +    * Extraire manuellement les archives
 +    * Utiliser [[https://postgis.net/docs/using_postgis_dbmanagement.html#shp2pgsql_usage| Shp2pgsql]] : 
 +<color #ed1c24>Note : le paquet postgis doit être isntallé pour pouvoir utiliser Shp2pgsql.</color>
 +      * M20 : ''shp2pgsql METROP_L9320X20.shp ref_geo.m20 | psql -h localhost -p 5432 -U geonatadmin -d gn2_default''
 +      * M50 : ''shp2pgsql METROP_L9350X50.shp ref_geo.m50 | psql -h localhost -p 5432 -U geonatadmin -d gn2_default''
 +    * Insérer les mailles M20 :<code sql>
 +INSERT INTO ref_geo.bib_areas_types (
 + type_name,
 + type_code,
 + type_desc,
 + ref_name,
 + ref_version,
 + num_version,
 + size_hierarchy
 +) VALUES (
 + 'Mailles20*20',
 + 'M20',
 + 'Type maille INPN 20*20km',
 + NULL,
 + 16082021,
 + NULL,
 + 20000
 +);
 +
 +INSERT INTO ref_geo.l_areas (
 + id_type,
 + area_name,
 + area_code,
 + geom,
 + geom_4326,
 + centroid,
 + "source",
 + "comment",
 + "enable",
 + additional_data
 +)
 + SELECT DISTINCT
 + ref_geo.get_id_area_type('M20'),
 + m.cd_sig,
 + m.code_20km,
 + m.geom,
 + st_transform(ST_SetSRID(m.geom, 2154), 4326),
 + st_centroid(m.geom),
 + 'INPN',
 + NULL,
 + TRUE,
 + NULL
 + FROM ref_geo.m20 AS m 
 + JOIN ref_geo.tmp_subdivided_areas AS a 
 + ON (m.geom && a.geom AND a.type_code = 'SINP') ;
 +
 +CREATE TABLE gn_synthese.cor_area_synthese_m20 PARTITION OF gn_synthese.cor_area_synthese_partitioned
 +     FOR VALUES IN ('M20');
 +</code>
 +    * Insérer les mailles M50 :<code sql>
 +INSERT INTO ref_geo.bib_areas_types (
 + type_name,
 + type_code,
 + type_desc,
 + ref_name,
 + ref_version,
 + num_version,
 + size_hierarchy
 +) VALUES (
 + 'Mailles50*50',
 + 'M50',
 + 'Type maille INPN 50*50km',
 + NULL,
 + 16082021,
 + NULL,
 + 50000
 +);
 +
 +
 +INSERT INTO ref_geo.l_areas (
 + id_type,
 + area_name,
 + area_code,
 + geom,
 + geom_4326,
 + centroid,
 + "source",
 + "comment",
 + "enable",
 + additional_data
 +)
 + SELECT DISTINCT
 + ref_geo.get_id_area_type('M50'),
 + m.cd_sig,
 + m.code_50km,
 + m.geom,
 + st_transform(ST_SetSRID(m.geom, 2154), 4326),
 + st_centroid(m.geom),
 + 'INPN',
 + NULL,
 + TRUE,
 + NULL
 + FROM ref_geo.m50 AS m 
 +            JOIN ref_geo.tmp_subdivided_areas AS a 
 +                ON (m.geom && a.geom AND a.type_code = 'SINP') ; 
 +
 +CREATE TABLE gn_synthese.cor_area_synthese_m50 PARTITION OF gn_synthese.cor_area_synthese_partitioned
 +     FOR VALUES IN ('M50');
 +</code>
 +  * Si besoins mettre à jour le champ ''area_type_code'' : <code sql>
 +WITH m20 AS (
 + SELECT id_area 
 + FROM ref_geo.l_areas 
 + WHERE id_type = ref_geo.get_id_area_type('M20')
 +)
 +UPDATE gn_synthese.cor_area_synthese AS cas SET
 + area_type_code = 'M20'
 +FROM m20
 +WHERE cas.id_area = m20.id_area;
 +
 +WITH m50 AS (
 + SELECT id_area 
 + FROM ref_geo.l_areas 
 + WHERE id_type = ref_geo.get_id_area_type('M50')
 +)
 +UPDATE gn_synthese.cor_area_synthese AS cas SET
 + area_type_code = 'M50'
 +FROM m50
 +WHERE cas.id_area = m50.id_area;
 +</code>
 +  * Activer seulement les mailles M20 de la région SINP : <code sql>
 +UPDATE ref_geo.l_areas AS a
 + SET "enable" = FALSE
 +WHERE a.id_type = ref_geo.get_id_area_type('M20');
 +
 +UPDATE ref_geo.l_areas AS a
 + SET "enable" = TRUE
 +FROM ref_geo.tmp_subdivided_areas AS sa 
 +WHERE a.id_type = ref_geo.get_id_area_type('M20'
 + AND a.geom && sa.geom 
 + AND sa.type_code = 'SINP'; 
 +</code>
 +  * Activer seulement les mailles M50 de la région SINP :<code sql>
 +UPDATE ref_geo.l_areas AS a
 + SET "enable" = FALSE
 +WHERE a.id_type = ref_geo.get_id_area_type('M50');
 +
 +UPDATE ref_geo.l_areas AS a
 + SET "enable" = TRUE
 +FROM ref_geo.tmp_subdivided_areas AS sa 
 +WHERE a.id_type = ref_geo.get_id_area_type('M50'
 + AND a.geom && sa.geom 
 + AND sa.type_code = 'SINP';
 +</code>
 +
 +==== Annexe 23 - Notes sur les outils utilisés durant les tests ====
 +
 +=== Utilisation de pg_stat_statements ===
 +  * Ressource : [[https://docs.postgresql.fr/15/pgstatstatements.html| Doc Postgresql 15 "pg_stat_statements", en français]]
 +  * ''pg_stat_statements'' est une extension Postgresql qui permet d'avoir un historique des requêtes lancés, leur fréquence, durée...
 +    * Étant données que les index spécialisées doivent être créé en fonction des requêtes les plus fréquentes, cet outil va nous servir à les identifiés et suivre leurs évolutions.
 +  * Procédure de mise en place :
 +    * Définir les paramètres de config Postgresql suivant : <code>
 +shared_preload_libraries = 'pg_stat_statements'
 +compute_query_id = on
 +pg_stat_statements.max = 10000
 +pg_stat_statements.track = all
 +</code>
 +    * Ajouter l'extension suivante à la base de données à surveiller : <code sql>CREATE EXTENSION pg_stat_statements ;</code>
 +    * Vous pourrez ensuite consultez les requêtes exécutés avec : <code sql>
 +SELECT pd.datname, pr.rolname, pss.query, pss.calls, (pss.mean_exec_time/1000) AS sec_mean_exec_time
 +FROM pg_stat_statements AS pss
 + JOIN pg_roles AS pr ON (pr."oid" = pss.userid)
 + JOIN pg_catalog.pg_database AS pd ON (pd."oid" = pss.dbid)
 +WHERE pr.rolname = 'geonatadmin'
 + AND pd.datname = 'gn2_default' 
 + AND pss.mean_exec_time > 500
 +ORDER BY pss.calls DESC, pss.mean_exec_time DESC;
 +</code>
 +  * ''pg_stat_statements'' permet de connaître les requêtes les plus fréquentes, ce qui permet ensuite de les analyser avec ''EXPLAIN (ANALYZE, COSTS, VERBOSE, BUFFERS, FORMAT JSON)'' et [[l'outil en ligne Explain de Dalibo| https://explain.dalibo.com/]].
 +  * Voir aussi [[https://www.crunchydata.com/blog/cleaning-up-your-postgres-database| cet article pour identifiez les index inutilisés]].
 +
 +=== Test du serveur de fond carto : Tileserver-GL ==
 +  * Utiliser Docker pour installer le TileServer GL. Debian ne possède pas nativement des paquets pour toutes les dépendances.
 +
 +=== Debugger de tuiles vecteurs ===
 +  * [[https://chromewebstore.google.com/detail/mfikcokdfehaofebfhoehbajfgbofdpk| Extension Chrome Mapbox Vector Tile]] : ajoute un onglet dans les outils développeur]]
 +  * [[https://stevage.github.io/vector-inspector/| Vector Inspector]]
 +  * [[https://openlayers.org/en/latest/examples/canvas-tiles.html| Affichage z,x,y des tuiles - OpenLayers]]
 +  * [[https://onthegomap.github.io/planetiler-demo/| Affichage z,x,y des tuiles - Maplibre Planetiler]]
 +
 +=== Framework carto web Maplibre ===
 +  * Création des outils de base de visualisation de la carte  très facile : zoom, passage en 3D, geolocalisation, 
 +  * Liste des plugins Maplibre GL : https://maplibre.org/maplibre-gl-js/docs/plugins/
 +    * Plugin d'outils d'édition //Mapbox-Gl-Draw// : https://github.com/mapbox/mapbox-gl-draw
 +      * Comment ajouter un mode (fonctionnalité d'édition) : 
 +      * Commenta ajouter une icone d'outil :
 +      * Mode édition de rectangle :
 +      * Mode édition de cercle : 
 +    * Plugin d'ajout de légende : https://github.com/watergis/mapbox-gl-legend
 +    * Plugin de gestion de couches et de leur opacité : https://github.com/mug-jp/maplibre-gl-opacity
 +    * Plugin tuiles GeoJson : https://github.com/mkeller3/mapbox-gl-ogc-feature-collection
 +    * Plugin d'inspection des features (debug) : https://github.com/maplibre/maplibre-gl-inspect
 +  * Intégration de Mapblibre avec Angular "@maplibre/ngx-maplibre-gl" : https://maplibre.org/ngx-maplibre-gl/demo/display-map
 +    * Documentation API Angular "@maplibre/ngx-maplibre-gl" : https://maplibre.org/ngx-maplibre-gl/doc
 +    * Code source des exemples Maplibre et Angular : https://github.com/maplibre/ngx-maplibre-gl/tree/c04efff199c1d21016c9bd2d6ba774286c5c3e77/projects/showcase/src/app/demo/examples
 +
 +==== Annexe 24 - Test d'utilisation de mailles hexagonales ====
 +  * Ressources : 
 +    * [[https://www.crunchydata.com/blog/tile-serving-with-dynamic-geometry| Tile Serving with Dynamic Geometry]] : Paul Ramsey, 24 mars 2020.
 +    * [[https://medium.com/geolytix/fishnets-honeycombs-and-footballs-better-spatial-models-with-hexagonal-grids-768bdf92d3bb| Fishnets, Honeycombs and Footballs; Better spatial models with hexagonal grids]] : Dennis Bauszus, 3 mars 2017.
 +    * [[https://medium.com/geolytix/hex-grid-algorithm-for-postgis-4ac45f61d093| Hex grid algorithm for PostGIS]] : Dennis Bauszus, 3 novembre 2017.
 +
 +Code utilisé :
 +<code sql>
 +CREATE OR REPLACE
 +FUNCTION
 +public.hexagon(i integer, j integer, edge float8)
 +RETURNS geometry
 +AS $$
 +DECLARE
 +h float8 := edge*cos(pi()/6.0);
 +cx float8 := 1.5*i*edge;
 +cy float8 := h*(2*j+abs(i%2));
 +BEGIN
 +RETURN ST_MakePolygon(ST_MakeLine(ARRAY[
 +            ST_MakePoint(cx - 1.0*edge, cy + 0),
 +            ST_MakePoint(cx - 0.5*edge, cy + -1*h),
 +            ST_MakePoint(cx + 0.5*edge, cy + -1*h),
 +            ST_MakePoint(cx + 1.0*edge, cy + 0),
 +            ST_MakePoint(cx + 0.5*edge, cy + h),
 +            ST_MakePoint(cx - 0.5*edge, cy + h),
 +            ST_MakePoint(cx - 1.0*edge, cy + 0)
 +         ]));
 +END;
 +$$
 +LANGUAGE 'plpgsql'
 +IMMUTABLE
 +STRICT
 +PARALLEL SAFE;
 +
 +CREATE OR REPLACE
 +FUNCTION public.hexagonCoordinates(bounds geometry, edge float8,
 +                            OUT i integer, OUT j integer)
 +RETURNS SETOF record
 +AS $$
 +    DECLARE
 +        h float8 := edge*cos(pi()/6);
 +        mini integer := floor(st_xmin(bounds) / (1.5*edge));
 +        minj integer := floor(st_ymin(bounds) / (2*h));
 +        maxi integer := ceil(st_xmax(bounds) / (1.5*edge));
 +        maxj integer := ceil(st_ymax(bounds) / (2*h));
 +    BEGIN
 +    FOR i, j IN
 +    SELECT a, b
 +    FROM generate_series(mini, maxi) a,
 +         generate_series(minj, maxj) b
 +    LOOP
 +       RETURN NEXT;
 +    END LOOP;
 +    END;
 +$$
 +LANGUAGE 'plpgsql'
 +IMMUTABLE
 +STRICT
 +PARALLEL SAFE;
 +
 +CREATE OR REPLACE
 +FUNCTION public.tileHexagons(z integer, x integer, y integer, step integer,
 +                      OUT geom geometry(Polygon, 3857), OUT i integer, OUT j integer)
 +RETURNS SETOF record
 +AS $$
 +    DECLARE
 +        bounds geometry;
 +        maxbounds geometry := ST_TileEnvelope(0, 0, 0);
 +        edge float8;
 +    BEGIN
 +    bounds := ST_TileEnvelope(z, x, y);
 +    edge := (ST_XMax(bounds) - ST_XMin(bounds)) / pow(2, step);
 +    FOR geom, i, j IN
 +    SELECT ST_SetSRID(hexagon(h.i, h.j, edge), 3857), h.i, h.j
 +    FROM hexagoncoordinates(bounds, edge) h
 +    LOOP
 +       IF maxbounds ~ geom AND bounds && geom THEN
 +            RETURN NEXT;
 +         END IF;
 +     END LOOP;
 +     END;
 +$$
 +LANGUAGE 'plpgsql'
 +IMMUTABLE
 +STRICT
 +PARALLEL SAFE;
 +
 +CREATE OR REPLACE
 +FUNCTION public.hexagons(z integer, x integer, y integer, step integer default 4)
 +RETURNS bytea
 +AS $$
 +WITH
 +bounds AS (
 +    -- Convert tile coordinates to web mercator tile bounds
 +    SELECT ST_TileEnvelope(z, x, y) AS geom
 + ),
 + rows AS (
 +    -- All the hexes that interact with this tile
 +    SELECT h.i, h.j, h.geom
 +    FROM TileHexagons(z, x, y, step) h
 + ),
 + mvt AS (
 +     -- Usual tile processing, ST_AsMVTGeom simplifies, quantizes,
 +     -- and clips to tile boundary
 +    SELECT ST_AsMVTGeom(rows.geom, bounds.geom) AS geom,
 +           rows.i, rows.j
 +    FROM rows, bounds
 +)
 +-- Generate MVT encoding of final input record
 +SELECT ST_AsMVT(mvt, 'hexagons') FROM mvt
 +$$
 +LANGUAGE 'sql'
 +STABLE
 +STRICT
 +PARALLEL SAFE;
 +
 +COMMENT ON FUNCTION public.hexagons IS 'Hex coverage dynamically generated. Step parameter determines how approximately many hexes (2^step) to generate per tile.';
 +
 +-- Test de requête
 +-- 10, 523, 369
 +WITH bounds AS (
 +    -- Convert tile coordinates to web mercator tile bounds
 +    SELECT ST_TileEnvelope(11, 1046, 739) AS geom
 +),
 +rows AS (
 +    -- All the hexes that interact with this tile
 +    SELECT
 +        h.i,
 +        h.j,
 +        h.geom,
 +        st_transform(h.geom, 4326) AS geom_4326
 +    FROM tileHexagons(9, 261, 184, 4) AS h
 + ),
 +observations AS (
 +    SELECT
 +        r.geom,
 +        r.i,
 +        r.j,
 +        COUNT(s.id_synthese) AS nbr
 +    FROM gn_synthese.synthese AS s
 +        JOIN "rows" AS r
 +            ON st_intersects(s.the_geom_4326, r.geom_4326)
 +    GROUP BY r.geom, r.i, r.j
 +)
 +-- Usual tile processing, ST_AsMVTGeom simplifies, quantizes,
 + -- and clips to tile boundary
 +SELECT
 +    ST_AsMVTGeom(o.geom, b.geom) AS geom,
 +    o.i,
 +    o.j,
 +    o.nbr
 +FROM observations AS o, bounds AS b 
 +WHERE ST_AsMVTGeom(o.geom, b.geom) IS NOT NULL;
 +</code>
 +
 +
 +**Résultats** : l'agrégation d'observations sur des mailles hexagonales générées dynamiquement est assez rapide aux niveaux de zoom 10 et plus. 
 +
 +==== Annexe 25 - Tests de requête SQL d'aggrégation ====
 +<code sql>
 +SELECT 
 +   t.id_area,
 +   t.area_code,
 +   t.area_name,
 +   t.geom
 +FROM ref_geo.l_areas AS t
 +WHERE ST_Intersects(t.geom, ST_Transform(ST_TileEnvelope(12, 2117, 1479), 2154))
 + AND t.id_type = ref_geo.get_id_area_type('COM');
 +
 +SELECT public.squares(12, 2117, 1479);
 +
 +-- Communes
 +SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 9 row(s) fetched - 0,010s (0,001s fetch), on 2024-03-19 at 16:32:36
 +SELECT ST_TileEnvelope(9, 264, 184) AS envelope; -- 155 row(s) fetched - 0,073s (0,019s fetch), on 2024-03-19 at 16:32:17
 +SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 408 row(s) fetched - 0,188s (0,071s fetch), on 2024-03-19 at 16:36:21
 +SELECT ST_TileEnvelope(7, 66, 46) AS envelope; -- 873 row(s) fetched - 0,366s (0,097s fetch), on 2024-03-19 at 16:35:29
 +SELECT ST_TileEnvelope(6, 33, 23) AS envelope; -- 1311 row(s) fetched - 0,490s (0,113s fetch), on 2024-03-19 at 16:37:22
 +SELECT ST_TileEnvelope(6, 32, 23) AS envelope; -- 6763 row(s) fetched - 1s (0,439s fetch), on 2024-03-19 at 16:37:55
 +SELECT ST_TileEnvelope(5, 16, 11) AS envelope; -- 24849 row(s) fetched - 4s (0,924s fetch), on 2024-03-19 at 16:31:44
 +SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 30521 row(s) fetched - 4s (0,690s fetch), on 2024-03-19 at 16:34:23
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(8, 132, 92) AS envelope
 +)
 +SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom, 
 +       t.id_area,
 +       t.area_code,
 +       t.area_name
 +FROM ref_geo.l_areas AS t, bounds AS b
 +WHERE t.geom && ST_Transform(b.envelope, 2154)
 + AND t.id_type = ref_geo.get_id_area_type('COM') ;
 +
 +-- Maille M1
 +SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 72 row(s) fetched - 0,003s (0,001s fetch), on 2024-03-19 at 16:57:41
 +SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 13161 row(s) fetched - 0,211s (0,079s fetch), on 2024-03-19 at 16:59:17
 +SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 481502 row(s) fetched - 6s (3s fetch), on 2024-03-19 at 17:00:30
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(4, 8, 5) AS envelope
 +)
 +SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom, 
 +       t.id_area,
 +       t.area_code,
 +       t.area_name
 +FROM ref_geo.l_areas AS t, bounds AS b
 +WHERE t.geom && ST_Transform(b.envelope, 2154)
 + AND t.id_type = ref_geo.get_id_area_type('M1') ;
 +
 +
 +-- Maille M5
 +SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 6 row(s) fetched - 0,005s (0,001s fetch), on 2024-03-19 at 16:58:06
 +SELECT ST_TileEnvelope(8, 132, 92) AS envelope; --  533 row(s) fetched - 0,021s (0,006s fetch), on 2024-03-19 at 16:58:32
 +SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 18856 row(s) fetched - 0,329s (0,119s fetch), on 2024-03-19 at 17:01:26
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(4, 8, 5) AS envelope
 +)
 +SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom, 
 +       t.id_area,
 +       t.area_code,
 +       t.area_name
 +FROM ref_geo.l_areas AS t, bounds AS b
 +WHERE t.geom && ST_Transform(b.envelope, 2154)
 + AND t.id_type = ref_geo.get_id_area_type('M5') ;
 +
 +-- Maille M10
 +SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope; -- 2 row(s) fetched - 0,002s, on 2024-03-19 at 17:03:21
 +SELECT ST_TileEnvelope(8, 132, 92) AS envelope; -- 156 row(s) fetched - 0,005s (0,001s fetch), on 2024-03-19 at 17:02:58
 +SELECT ST_TileEnvelope(4, 8, 5) AS envelope; -- 6013 row(s) fetched - 0,088s (0,036s fetch), on 2024-03-19 at 17:02:07
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope
 +)
 +SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom, 
 +       t.id_area,
 +       t.area_code,
 +       t.area_name
 +FROM ref_geo.l_areas AS t, bounds AS b
 +WHERE t.geom && ST_Transform(b.envelope, 2154)
 + AND t.id_type = ref_geo.get_id_area_type('M10') ;
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(12, 2117, 1479) AS envelope
 +),
 +observations AS (
 + SELECT 
 + l.area_code AS code,
 + ST_Transform(l.geom, 3857) AS geom,
 + COUNT(s.id_synthese) AS nbr
 + FROM gn_synthese.synthese AS s 
 + JOIN gn_synthese.cor_area_synthese AS cas 
 + ON s.id_synthese = cas.id_synthese 
 + JOIN ref_geo.l_areas AS l
 + ON l.id_area = cas.id_area 
 + , bounds AS b
 + WHERE l.geom && ST_Transform(b.envelope, 2154)
 + AND l.id_type = ref_geo.get_id_area_type('M10')
 + GROUP BY l.area_code, l.geom
 +)
 +SELECT ST_AsMVTGeom(o.geom, b.envelope) AS geom, 
 +       o.nbr,
 +       o.code 
 +FROM observations AS o, bounds AS b ;
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(9, 264, 184) AS envelope
 +)
 +SELECT 
 + l.area_code AS code,
 + ST_Transform(l.geom, 3857) AS geom,
 + COUNT(s.id_synthese) AS nbr
 +FROM gn_synthese.synthese AS s 
 + JOIN gn_synthese.cor_area_synthese AS cas 
 + ON s.id_synthese = cas.id_synthese 
 + JOIN ref_geo.l_areas AS l
 + ON l.id_area = cas.id_area 
 + , bounds AS b
 +WHERE l.geom && ST_Transform(b.envelope, 2154)
 + AND l.id_type = ref_geo.get_id_area_type('M10')
 +GROUP BY l.area_code, l.geom ; -- 32 row(s) fetched - 7s (0,001s fetch), on 2024-03-19 at 17:20:06
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(9, 264, 184) AS envelope
 +)
 +SELECT l.id_area, l.geom, l.area_code
 +FROM ref_geo.l_areas AS l, bounds AS b
 +WHERE l.geom && ST_Transform(b.envelope, 2154)
 + AND l.id_type = ref_geo.get_id_area_type('M10') ; -- 32 row(s) fetched - 0,007s (0,001s fetch), on 2024-03-19 at 17:29:37
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(9, 264, 184) AS envelope
 +),
 +m10 AS (
 + SELECT 
 + l.id_area, 
 + l.geom, 
 + l.area_code AS code
 + FROM ref_geo.l_areas AS l, bounds AS b
 + WHERE l.geom && ST_Transform(b.envelope, 2154)
 + AND l.id_type = ref_geo.get_id_area_type('M10')
 +)
 +SELECT 
 + m10.code,
 + ST_Transform(m10.geom, 3857) AS geom,
 + COUNT(s.id_synthese) AS nbr
 +FROM gn_synthese.synthese AS s
 + JOIN gn_synthese.cor_area_synthese AS cas 
 + ON s.id_synthese = cas.id_synthese
 + JOIN m10 
 + ON cas.id_area = m10.id_area
 +GROUP BY m10.code, m10.geom ; -- 32 row(s) fetched - 7s, on 2024-03-19 at 17:39:22
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(9, 264, 184) AS envelope
 +),
 +m10 AS (
 + SELECT l.id_area, l.geom, l.area_code AS code
 + FROM ref_geo.l_areas AS l, bounds AS b
 + WHERE l.geom && ST_Transform(b.envelope, 2154)
 + AND l.id_type = ref_geo.get_id_area_type('M10')
 +)
 +SELECT 
 + m10.code,
 + ST_Transform(m10.geom, 3857) AS geom,
 + COUNT(s.id_synthese) AS nbr
 +FROM gn_synthese.synthese AS s
 + JOIN m10 
 + ON s.the_geom_local && m10.geom
 +GROUP BY m10.code, m10.geom ; -- 32 row(s) fetched - 27s (0,001s fetch), on 2024-03-19 at 17:35:57
 +
 +
 +WITH bounds AS (
 + SELECT ST_TileEnvelope(8, 132, 92) AS envelope
 +), mvtgeom AS (
 + SELECT ST_AsMVTGeom(ST_Transform(t.geom, 3857), b.envelope) AS geom, 
 +        t.id_area,
 +        t.area_code,
 +        t.area_name
 + FROM ref_geo.l_areas AS t, bounds AS b
 + WHERE ST_Intersects(t.geom, ST_Transform(b.envelope, 2154))
 + AND t.id_type = ref_geo.get_id_area_type('COM')
 +)
 +SELECT ST_AsMVT(mvtgeom.*) FROM mvtgeom ;
 </code> </code>
  • fonctionnalites/geonature/synthese-amelioration-performance-test.1719312009.txt.gz
  • Dernière modification : 2024/06/25 10:40
  • de jpmilcent