Rechercher dans le blog

Un mardi, une appli #24 : l'Hexagone en hexagones

Pendant tout le mois de novembre a lieu un challenge palpitant, le #30DayMapChallenge. Avec quelques-uns de mes collègues, nous avons décidé de former une petite équipe pour relever ce défi ensemble ! Il revient à chacun d'entre nous de réaliser 3 cartes, et pour ma part, j'ai décide de faire chacune d'entre elles en y ajoutant une petite touche de JavaScript ou de Python. La première de mes cartes est celle du jour 4, sur le thème Hexagones. L'occasion d'explorer une thématique assez commune, les données d'élévation, sous une forme originale. Dans cet article, je vous propose de découvrir cette carte, mais aussi d'apprendre à réaliser la même chose par vous-même à travers un tutoriel détaillé (qui utilisera notamment l'API JavaScript d'ArcGIS, mais à un niveau très accessible).

L'application

Voici l'application que j'ai créée, que vous pouvez consulter en pleine page pour un meilleur confort. J'aime beaucoup le rendu de cette carte, qui me fait penser notamment au jeu de plateau Catan, que je vous conseille fortement si vous souhaitez perdre tous vos amis.
Puisque l'application est interactive, il est très aisé de se promener à l'intérieur pour obtenir des points de vue sur les différents massifs montagneux, comme je l'ai fait pour réaliser le poster ci-dessous :
Maintenant, restez avec moi si vous souhaitez reproduire ce tutoriel pour recréer votre propre carte, ce qui vous permettra de la personnaliser par exemple à l'échelle de votre région ou avec les couleurs de votre choix !

Le tutoriel

1/ Préparation et publication des données

Dans cette première étape, je vais vous expliquer comment j'ai transformé la donnée de base dans ArcGIS Pro et comment je l'ai publiée afin de pouvoir l'utiliser en donnée d'entrée pour mon application. Si vous n'avez pas ArcGIS Pro ou que vous n'êtes intéressé que par le code, vous pouvez passer directement à la deuxième partie ou je vous donne le lien direct vers la donnée finale prête à être utilisée.

Dans un premier temps, j'ai récupéré le MNT de la France métropolitaine et de la Corse sur data.gouv.fr, publié à partir des données SRTM de la NASA. Cette donnée est fournie au format .TIF. C'est donc un raster, donc chacune des cellule contient la moyenne d'élévation de la zone, que j'ajoute dans ArcGIS Pro. 
La donnée étant trop résolue par rapport à mon besoin, je vais commencer par rééchantillonner le raster en donnant des nouvelles valeurs de X et de Y égales à 0,01. Ainsi, je vais dégrader la résolution et obtenir des temps de traitement plus rapides.
J'utilise ensuite l'outil Raster vers points qui me permet de transformer cette donnée raster en donnée vecteur, en plaçant un point contenant la valeur d'élévation de chaque cellule au milieu de ladite cellule.
Enfin, je vais pouvoir agréger les points en groupes d'hexagones de 10 kilomètres. J'indique que chaque hexagone doit retourner comme valeur la moyenne d'élévation de tous les points contenus dans la zone. [Note : L'information d'élévation est contenue dans le champ grid_code].
J'ai réalisé ce workflow sur les données de la France entière, mais j'ai zoomé dans une petite partie des Alpes pour l'illustration ci-dessous :
Il est maintenant temps de publier mes données afin de pouvoir les partager en ligne. Vous devez pour cela posséder des privilèges de publication sur votre portail ArcGIS Online ou Enterprise. Retirez toutes les couches sauf la couche d'hexagones de votre projet, puis allez dans l'onglet Partager et sélectionnez l'option carte web afin de partager vos données sur le portail. Vous rencontrerez d'éventuels messages d'erreur lors du partage, qui sont tous résolvables en suivant les indications renvoyées par le message.
Voici la carte finale publiée sur mon portail : 
Il est temps de passer au code !

2/ Code

Je vais maintenant pouvoir créer une application pour permettre au grand public d'explorer les données que je viens de publier, et pour cela, je vais utiliser l'API JavaScript d'ArcGIS. Cette API va me permettre à la fois de travailler en 3D (très utile ici pour bien représenter l'élévation), de modifier le style de symbologie, et de permettre une navigation fluide dans les données, le tout avec un effort de développement très faible. L'API est accessible gratuitement, alors n'hésitez pas la à tester !
Tout le code que je vais vous présenter est comme d'habitude disponible sur mon Github, et vous pouvez également le tester de manière interactive dans ce CodePen si vous ne souhaitez pas le recopier de zéro. Si vous sentez que le niveau de ce tutoriel est un peu trop élevé pour vous et que les notions sont expliquées trop rapidement, je vous conseille d'explorer cette série qui prend le temps d'expliquer pas à pas les notions fondamentales. Cela étant dit, c'est parti pour le code !
 
Le code HTML est extrêmement simple, avec un appel à l'API JavaScript https://js.arcgis.com/4.30/ ainsi qu'à son style https://js.arcgis.com/4.30/esri/themes/dark/main.css, que nous allons utiliser ici en mode sombre (dark). Je référence également ma propre feuille de style et mon propre script que j'ai séparés du HTML. Dans le corps du HTML, j'insère une unique balise <viewDiv> qui contiendra la vue de ma carte. 
<html lang="en">

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
  <title>
    France | Élévation
  </title>
  <link rel="stylesheet" href="style/style.css" />


  <link rel="stylesheet" href="https://js.arcgis.com/4.30/esri/themes/dark/main.css" />
  <script src="https://js.arcgis.com/4.30/"></script>
  <script src="app/main.js"></script>

</head>

<body>
    <div id="viewDiv"></div>
</body>

</html>
Côté CSS, je m'assure simplement que la taille de la <viewDiv> prendra l'entièreté de l'écran.
html,
body,

#viewDiv {
  padding: 0;
  margin: 0;
  height: 100%;
  width: 100%;
  position: absolute;
}

Il est maintenant temps de faire appel à l'API JavaScript pour insérer notre carte ! 
Comme d'habitude, je commence par appeler les classes qui me seront utiles dans le require (ici, Map, SceneView et FeatureLayer), puis je déclare la fonction qui contiendra toutes les instructions utiles à l'affichage de ma carte et je lui passe en arguments chacune de ces classes dans le même ordre que dans le require.
require([
  "esri/Map",
  "esri/views/SceneView",
  "esri/layers/FeatureLayer",
], function (
  Map,
  SceneView,
  FeatureLayer,
) {
//le reste du code ira ici
});
Je crée ensuite une instance de Feature Layer, dont l'url renvoie vers le service publié à l'étape précédente. 
  const elevLayer = new FeatureLayer({
    url:
      "https://services.arcgis.com/d3voDfTFbHOCRwVR/arcgis/rest/services/Altitudes_hexagones_WFL1/FeatureServer/1",
    copyright: "SRTM - NASA",
    outFields: ["*"],
    elevationInfo: {
      mode: "relative-to-ground"
    }
  });
Si vous n'avez pas publié votre propre service, vous pouvez utiliser la même url que moi. Sinon, pour retrouver l'url de votre service, rendez-vous sur l'item correspondant au Feature Layer hébergé, puis scrollez jusqu'à atteindre l'URL comme ci-dessous :  
Ouvrez le lien dans une nouvelle fenêtre, puis dans la section Layers, cliquez sur la couche correspondant à votre couche d'hexagones (qui devrait être la seule).  Vous pouvez alors copier l'url et le remplacer dans le code.
Notez également que j'utilise la propriété elevationInfo  afin de spécifier de quelle manière les entités seront placées par rapport à la surface de ma scène 3D. Ici, nous utilisons le mode "relative-to-ground".
 
Je vais ensuite définir le Renderer de la couche, c'est-à-dire son rendu, la manière dont elle sera représentée et ce qui lui permettra de véhiculer l'information que je souhaite. Différents types de rendus sont possibles, ici, je vais utiliser un rendu simple (SimpleRenderer) associé à des variables visuelles (visualVariables).  
  elevLayer.renderer = {
    type: "simple", 
    symbol: {
      type: "polygon-3d",
      symbolLayers: [
        {
          type: "extrude" 
        }
      ]
    },

    visualVariables: [
      {
        type: "color",
        field: "Altitude_moyenne", 
        stops: [
          { value: -12, color: "#A1B696" },
          { value: 500, color: "#FCD170" },
          { value: 1000, color: "#647F2F" },
          { value: 1500, color: "#60562D" },
          { value: 2000, color: "#815F5B" },
          { value: 2500, color: "#DBCDCB" }
        ]
      },
      {
        type: "size",
        field: "Altitude_moyenne", 
        stops: [
          { value: 0, size: 1 },
          { value: 3300, size: 100000 }
        ],
        axis: "height"
      }
    ]
  };
Pour construire ce rendu, je commence par indiquer que le type de rendu utilisé est "simple" pour un SimpleRenderer. Je dois ensuite définir le symbole utilisé. Puisque je travaille en 3D sur une donnée polygonale, je vais utiliser la classe PolygonSymbol3D, et j'indique pour cela que le type de symbole est  "polygon-3d". Je peux ensuite indiquer le symbolLayers, permettant de définir la manière dont les objets seront visualisés. Ici, nous définirons le type sur "extrude" afin d'obtenir une extrusion 3D.
Enfin, pour faire varier la couleur et la hauteur de l'extrusion selon l'altitude moyenne de l'hexagone, je vais me baser sur des visualVariables
La première sera une ColorVariable pour faire varier la couleur. J'indique cela en paramétrant le type sur "color", puis je défini le champ (field) selon lequel la couleur devra varier, chez moi Altitude_moyenne qui contient l'altitude. Je peux ensuite indiquer les stops, qui sont un array contenant des valeurs clés auxquelles j'attribue des valeurs précises. Ici par exemple, je définis 12 valeurs d'altitudes qui prendront une couleur particulière que je passe avec sa valeur hexadécimale (j'aurais également pu utiliser du rgb). Entre ces valeurs, l'API va déterminer automatiquement une rampe de couleurs pour attribuer des couleurs à chacun de mes polygones. 
Je répète l'opération une seconde fois en utilisant cette fois-ci une SizeVariable, pour cette fois-ci faire en sorte que la hauteur d'extrusion de mes polygones soit plus importante à mesure que leur valeur d'altitude augmente.

Voilà, le plus dur est fait ! Afin d'afficher cette couche, je crée maintenant une instance de carte (Map) dans laquelle je passe dans la propriété layers la couche que je viens de paramétrer juste avant. Je vais ensuite paramétrer le Ground afin d'empêcher la navigation sous la carte avec la propriété navigationConstraint paramétrée sur "stay-above", et j'indique également que je ne souhaite pas qu'il soit visible en réglant son opacité (opacity) sur 0.
  var map = new Map({
    layers: [elevLayer],
    ground: {
      navigationConstraint: {
        type: "stay-above"
      },
      opacity: 0
    }
  });
 
Afin de pouvoir voir ma carte, je vais devoir afficher celle-ci dans une SceneView, qui me permet de générer une vue 3D interactive de ma carte. 
    var view = new SceneView({
    container: "viewDiv",
    map: map,
    camera: {
      heading: -5, 
      tilt: 50,
      position: {
        latitude: 27.5,
        longitude: 5,
        z: 2300000, 
        spatialReference: { wkid: 3857 }
      }
    },
    viewingMode: "local",
    environment: {
      background: {
        type: "color",
        color: "#212526"
      },
      atmosphereEnabled: false,
      starsEnabled: false
    }
  });
Je commence par indiquer le container, la balise de mon HTML dans laquelle la carte sera affichée, ici "viewDiv" comme nous l'avons défini au tout début du tutoriel. J'indique également que la carte (map) sur laquelle je génère la vue est la carte créée juste ci-dessus. 
Je dois ensuite placer la camera, qui donnera l'angle initial avec lequel je dois virtuellement filmer ma carte. Pour cela, je définis une orientation par rapport au Nord (heading), une inclinaison (tilt), la position constituée de la longitude, de la latitude et de l'élévation (z) ainsi qu'une référence spatiale (spatialReference). 
Je peux également déterminer qu'il s'agit d'une scène locale grâce au viewingMode, et modifier l'environment en choisissant une couleur d'arrière-plan (background) ainsi qu'en supprimant l'atmosphère et les étoiles présentes par défaut.
Enfin, je retire également les widgets de l'interface utilisateurs présents par défaut afin d'épurer l'application au maximum.
  view.ui.remove([ "compass", "zoom","navigation-toggle"]);
 
Et voilà, vous avez votre carte interactive ! Vous pouvez désormais ajuster l'HTML et le CSS afin d'obtenir plus d'éléments d'interface, par exemple un titre ou des crédits, comme je l'ai fait pour mon application. Si vous avez besoin d'un coup de pouce ou du code complet, n'hésitez pas à nouveau à consulter le code complet sur mon Github ou dans ce CodePen.
Si cet article vous a plus, n'hésitez pas à retrouver les tutoriels de mes deux autres créations cartographiques pour ce challenge : une animation sur les variations d'étendues de l'Arctique, et une carte d'isohypses pour représenter les concentrations des photos que j'ai prises dans ma ville.

Aucun commentaire:

Enregistrer un commentaire