Catégorie: Développement

Un million d’éléments sur une carte interactive, mode d’emploi.

Auteur(s) de l'article

Kevin
Kevin
Récemment, l’un de nos clients, une société immobilière, nous a lancé un défi. Ils souhaitaient une carte interactive présentant l’ensemble de leurs propriétés à travers le monde. Le hic ? Ils possèdent plus d’un million de propriétés. Et c’est ainsi que notre aventure a commencé.
Final map showcasing the East Coast of North America.
Le résultat de notre carte. Continuez à lire pour découvrir comment nous avons réalisé cela 🥸
À première vue, cela semblait simple. Le véritable défi n’était pas seulement d’afficher des millions de propriétés, mais de le faire de manière rapide et efficace. Une carte qui charge les marqueurs lentement ou qui plante sous le poids des données serait un désastre.

Le scope

Les exigences initiales sont assez simples :
  1. Afficher des millions de propriétés à travers le monde.
  2. Montrer des clusters de propriétés lorsque beaucoup se trouvent à proximité, en s’ajustant dynamiquement au fur et à mesure que les utilisateurs zooment ou dézooment.
  3. La carte doit être extrêmement rapide, quel que soit l’emplacement ou le nombre de propriétés visibles à l’écran.
Je sentais que ce ne serait pas simplement une question de récupérer des données depuis notre Elasticsearch et de les afficher sur une carte.
Imaginez essayer d’afficher chaque propriété comme un marqueur. Le résultat serait une mer de points indéchiffrable, submergeant à la fois la carte et l’utilisateur.
A Map full of markers
beaucoup de monde ici, n’est-ce pas ?
Même avec un clustering côté client, le véritable défi serait la taille de nos données. Imaginez simplement le poids d’une requête API contenant des millions de marqueurs — ce serait un cauchemar pour le transfert de données et le pire ennemi de votre forfait mobile.

Nous devions trouver une solution qui équilibre performance, évolutivité et fonctionnalité.

A Map with client-side clustering only
Tout ce dont la plupart des projets auront besoin : le clustering côté client 📍

Brainstorming

Nous avions besoin d’une solution qui équilibre performance, évolutivité et fonctionnalités. Nous avons donc commencé par décomposer le problème.

Filtrage par Bounding Box

Pour réduire le nombre d’éléments demandés depuis notre index, nous avons décidé d’imposer à l’application frontend de fournir une boîte englobante. Cette boîte englobante représenterait la portion visible de la carte sur l’écran de l’utilisateur, en filtrant toutes les propriétés en dehors de cette zone.
A map with a transparent rectangle over it that attempts to explain what is a Bounding Box.
La bounding box est un système de coordonnées rectangulaires utilisé pour définir une zone géographique sur les cartes.
Cependant, cela ne suffira pas à lui seul — si un utilisateur dézoome pour voir le monde entier, le jeu de données pourrait encore être énorme.

Clustering

Ici, nous abordons le volume de données. Des millions de propriétés signifiaient des millions de points de données individuels. Envoyer tout cela vers le frontend était hors de question. Le clustering côté serveur était la solution évidente.
Au lieu d’afficher chaque propriété individuellement, nous allions regrouper les propriétés proches et afficher un seul marqueur avec un nombre représentant le total. Cela réduirait considérablement le nombre d’éléments que notre API backend devait envoyer, rendant la carte plus lisible et efficace.
En utilisant Elasticsearch, nous avons mis en place l’agrégation par Geotile grid pour gérer le clustering de nos géodonnées. Cela nous a permis d’agréger les propriétés en clusters basés sur leur emplacement géographique.

UX User Experience

Puis une autre question s’est posée : comment pourrions-nous mélanger de manière fluide à la fois des clusters et des marqueurs individuels ? L’agrégation par Geotile grid d’Elasticsearch ne nous permettait pas d’exposer à la fois des marqueurs et des clusters simultanément via notre API. Nous étions déterminés à offrir la meilleure expérience utilisateur possible, donc avoir des clusters d’un seul élément au lieu d’afficher le marqueur réel n’était pas une option (du moins pas à grande échelle)
A map full of clusters containg a single marker
Ce qui peut résulter d’une stratégie de clustering côté serveur, c’est d’avoir des clusters ne contenant qu’un seul élément.
Après une bonne discussion autour d’un café, nous avons élaboré une stratégie pour combiner le clustering côté serveur et côté client.
The meme “Tim And Eric’s Mind Blown”
Mix server-side et client-side clustering 🤯
Nous avons créé deux endpoints:
  • /clusters pour la navigation à un niveau élevé (niveaux de zoom 0-10) où les propriétés sont fortement regroupées. Cependant, ce endpoint peut encore renvoyer des clusters ne contenant qu’une seule propriété.
  • /markers pour une inspection plus approfondie (niveaux de zoom 11-30) où les propriétés individuelles peuvent être affichées.
À des niveaux de zoom plus élevés, nous n’afficherions que des clusters, car chaque tuile de la carte pourrait contenir des milliers d’éléments. Mais à mesure que les utilisateurs zoomaient davantage, le système passerait à l’affichage de marqueurs individuels, garantissant une expérience fluide et réactive.
Cette stratégie nous a permis d’afficher efficacement plus d’un million de propriétés sur une carte, sans compromettre les performances ni l’expérience utilisateur. C’est un équilibre parfait entre innovation et pragmatisme, et cela a prouvé qu’avec la bonne approche, même les défis les plus redoutables peuvent être surmontés.

Implémentation backend

Pour les passionnés de technologie au premier rang, ceci est pour vous. Plongeons dans la mise en œuvre technique de la façon dont nous avons réussi à cartographier plus d’un million de propriétés. Je vais vous expliquer les composants clés de notre configuration Elasticsearch.
Je ne montrerai qu’un sous-ensemble de notre requête pour mettre en évidence certaines parties spécifiques.

Configuration de l’index Elasticsearch

Tout d’abord, pour gérer les données géographiques, votre index Elasticsearch doit être configuré avec une propriété typée comme geo_point. Ce type permet à Elasticsearch de gérer nativement les emplacements géographiques.
PUT /realestate
{
  "mappings": {
    "properties": {
      "location": {
        "type": "geo_point"
      }
    }
  }
}
Cette configuration nous permet de stocker les coordonnées de latitude et de longitude pour chaque propriété, ce qui est crucial pour les fonctionnalités de clustering et de marqueurs qui suivent..

Markers Endpoint

Dans les cas où l’utilisateur zoome de près, nous devons afficher des marqueurs individuels au lieu de clusters. Ce endpoint gère la récupération des propriétés spécifiques dans notre fameuse Bounding Box.
Voici une version de notre requête avec un sous-ensemble de bouding-box:
POST /realestate/_search
{
  "query": {
    "bool": {
      "must": {
        "match_all": {}
      },
      "filter": {
        "geo_bounding_box": {
          "pin.location": {
            "top_left": {
              "lat": 40.73,
              "lon": -74.1
            },
            "bottom_right": {
              "lat": 40.01,
              "lon": -71.12
            }
          }
        }
      }
    }
  }
}
La réponse fournit les coordonnées détaillées de chaque propriété :
[  
  {
   "lat": -34.936909378343,
   "lon": -54.9183369647971,
   "id": "0000c086-fa6d-4e2a-a029-d5af313c7d3f"
  },
  {
   "lat": 49.1631672,
   "lon": -122.0157818,
   "id": "000a2251-dca9-4763-8d69-a8a8c1f7036a"
  },
  {
   "lat": 31.903325,
   "lon": -95.3027039,
   "id": "00116edc-95e0-4a35-8ca3-340344a16620"
  }
  // A millions times ...
]
Ce endpoint nous permet de fournir des données détaillées pour les niveaux de zoom où les clusters ne sont plus nécessaires, garantissant une transition fluide entre les différentes vues de la carte.

Clusters Endpoint

Pour éviter de surcharger le frontend avec des millions de marqueurs individuels, nous avons décidé d’agréger les propriétés en clusters en fonction de leur emplacement géographique. Pour cela, nous avons utilisé l’agrégation geotile_grid d’Elasticsearch. Cette agrégation divise la carte en tuiles, regroupant les propriétés à l’intérieur de chaque tuile.
Voici une version simplifiée de notre requête :
POST /realestate/_search?size=0
{
  "aggregations": {
    "large-grid": {
      "geotile_grid": {
        "field": "location",
        "precision": 8
      }
    }
  }
}
Le paramètre de précision détermine la granularité de la grille — une précision plus élevée signifie des tuiles plus petites. La réponse pourrait ressembler à ceci :
[  
  {
    "key": "8/131/84",
    "doc_count": 3
  },
  {
    "key": "8/129/88",
    "doc_count": 2
  },
  {
    "key": "8/131/85",
    "doc_count": 1
  }
]
Pour faciliter le placement précis des clusters sur le frontend, nous avons calculé le centre géographique, ou centroïde, de chaque cluster. De cette manière, le frontend peut afficher le marqueur du cluster à l’emplacement le plus représentatif dans la grille, sans avoir à le calculer côté client.
POST /realestate/_search?size=0
{
  "aggregations": {
    "large-grid": {
      "geotile_grid": {
        "field": "location",
        "precision": 8
      },
      "aggs": {
        "centroid": {
          "geo_centroid": {
            "field": "location" 
          }
        }
      }
    }
  }
}
La réponse inclut désormais à la fois le nombre de clusters et les coordonnées du centroïde :
[  
  {
   "key": "20/543807/370868",
   "doc_count": 8,
   "geo_centroid": {
    "location": {
     "lat": 46.51612099260092,
     "lon": 6.701349942013621
    }
   }
  },
  {
   "key": "20/545343/372639",
   "doc_count": 4,
   "geo_centroid": {
    "location": {
     "lat": 46.096099968999624,
     "lon": 7.228809949010611
    }
   }
  }
]

Conclusion

En mettant en œuvre stratégiquement ces requêtes et points de terminaison Elasticsearch, et en combinant un mélange de clustering côté client et côté serveur, nous avons réussi à créer une interface de carte hautement efficace, capable d’afficher des millions de propriétés sans sacrifier les performances. Que ce soit pour le clustering à un niveau de zoom élevé ou pour l’affichage de marqueurs individuels de près, notre solution garantit une expérience fluide et réactive.
The meme "Success Kid".
Success ! We make it 🎉
Votre projet implique-t-il l’affichage de données massives sur une carte interactive ? Contactez-nous — nous pouvons vous aider à concevoir votre index Elasticsearch, développer la carte ou vous fournir des retours sur votre intégration existante.

Sources

Arthur C. Codex (2024) Why Clustering your Google Map markers in the server side?
reintech.io/…/handling-large-datasets-...-marker-clustering
Elastic (2023) Comparing Geotile and Geohex in Kibana.
youtube.com/watch?v=ucI6OeYlu50
Mateusz Jasiński (2022) Clustering on maps with Elasticsearch.
medium.com/@mateusz-jasinski/clustering-on-maps
Louis Z. (2021) Server-side Map Clustering.
louisz.medium.com/server-side-map-clustering
Victor Barzana (2020) Why Clustering your Google Map markers in the server side?
viticoinf.medium.com/why-clustering-your-google-map-markers
Ion Bazan (Apr, 2021) Efficiently resolving composer.lock merge conflicts.
https://dev.to/ionbazan/efficiently-resolving-composer-lock-merge-conflicts-1peb
Mika Tuupola (2008) Introduction to Marker Clustering With Google Maps
appelsiini.net/2008/introduction-to-marker-clustering-with-google-maps

Resources

ChatGPT, http://chat.openapi.com
Pour aider à la rédaction et à la traduction
Claude AI, https://claude.ai
Pour la génération du code pour expliquer les concepts

Continuer ma lecture

Auteur(s) de l'article

Akira
Akira

Making of : “La nuit des Losers”

Le site de « La Nuit des Losers », fait en un temps record : wireframes papier, animations testées en direct avec Claude Code, un flipper abandonné, un tableau de réactions un peu triché, et deux-trois bugs qu’on garde précieusement. Ce que l’IA a vraiment changé ici, et là où elle a calé.
Le site de « La Nuit des Losers », fait en un temps record : wireframes papier, animations testées en direct avec Claude Code, un flipper abandonné, un tableau de réactions un peu triché, et deux-trois bugs qu’on garde précieusement. Ce que l’IA a vraiment changé ici, et là où elle a calé.
Making of : “La nuit des Losers”

Auteur(s) de l'article

Pierre
Pierre

Dark mode : nécessité ou effet de mode

Nous faisons le tri entre idées reçues, réalités techniques et bonnes pratiques de design.
Nous faisons le tri entre idées reçues, réalités techniques et bonnes pratiques de design.
Dark mode : nécessité ou effet de mode

Auteur(s) de l'article

Susana
Susana

L’IA pour la gestion de projet

En tant que chef·fe de projet en agence web, on entend beaucoup parler de l’IA et de ce fameux Claude, réputé pour accompagner efficacement les développeur·euses dans leur quotidien. Mais \[…\]
En tant que chef·fe de projet en agence web, on entend beaucoup parler de l’IA et de ce fameux Claude, réputé pour accompagner efficacement les développeur·euses dans leur quotidien. Mais \[…\]
L’IA pour la gestion de projet