Chaque marque de commerce de détail, franchise et entreprise de services a besoin tôt ou tard d'un localisateur de magasin. C'est la page que les clients visitent quand ils veulent déjà acheter et ont juste besoin de savoir dans quelle succursale entrer. Mal faire cette page coûte de vraies conversions. La faire correctement est plus simple que la plupart des développeurs ne le pensent.
L'approche standard a été Google Maps, mais cela implique une structure de coûts qui pique à grande échelle. Un site de vente au détail très fréquenté avec 50 000 visiteurs mensuels chargeant une page de carte peut accumuler des centaines d'euros de charges de chargement de carte et de géocodage du jour au lendemain. Il n'y a plus de tier gratuit pour la production, la facturation est opaque, et la situation RGPD pour les entreprises de l'UE utilisant un service de cartographie américain ajoute une charge de conformité supplémentaire.
Ce tutoriel construit un localisateur de magasin complet : carte interactive, recherche d'adresse, panneau de liste synchronisé, regroupement de marqueurs et popups de détail au clic, en utilisant l'API MapAtlas Maps et l'API de géocodage. Aucun framework requis. L'exemple fonctionnel complet tient en environ 80 lignes de HTML et JavaScript. Vous pouvez l'intégrer dans un bloc HTML personnalisé WordPress, une section Shopify, ou n'importe quel CMS l'après-midi même où vous lisez ceci.
À la fin vous aurez :
- Une carte vectorielle rendue centrée sur votre réseau de magasins
- Des marqueurs chargés depuis un tableau de données JSON avec regroupement à faible zoom
- Une barre de recherche d'adresse alimentée par l'API de géocodage
- Un panneau de liste synchronisé qui se surligne au clic sur la carte
- Une mise en page à deux colonnes adaptée aux mobiles
Ce dont un localisateur de magasin a réellement besoin
Avant d'écrire une ligne de code, il est utile d'être précis sur les exigences. Un localisateur de magasin fonctionnel a quatre composants :
- Une carte qui affiche des tuiles, accepte le déplacement et le zoom, et montre des marqueurs.
- Un champ de recherche qui géocode l'adresse saisie par l'utilisateur en coordonnées, puis recentre la carte.
- Une couche de marqueurs qui trace l'emplacement de chaque magasin, regroupe les épingles proches à faible zoom, et ouvre une popup de détail au clic.
- Un panneau de liste qui affiche les magasins triés par distance depuis l'emplacement recherché, surligne le magasin actif, et défile en synchronisation avec la carte.
C'est tout. Chaque autre fonctionnalité, itinéraires, horaires d'ouverture, stock d'inventaire, est une amélioration superposée sur ces quatre éléments. Construisez d'abord le coeur.
Étape 1 : Charger le SDK MapAtlas
L'API MapAtlas Maps est compatible avec l'interface Mapbox GL JS, donc n'importe quel tutoriel ou plugin Mapbox GL JS fonctionne directement. Ajoutez les liens CDN au <head> de votre page :
<link
rel="stylesheet"
href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css"
/>
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
Si vous utilisez npm :
npm install @mapmetrics/mapmetrics-gl
Obtenez votre clé API gratuite sur portal.mapmetrics.org/signup. Une seule clé couvre les tuiles de carte, le géocodage et le routage, sans identifiants séparés à gérer.
Étape 2 : Définir les données de vos magasins
Les données de magasins sont simplement une FeatureCollection GeoJSON. Chaque feature porte les coordonnées du magasin et toutes les propriétés dont votre popup a besoin : nom, adresse, téléphone, horaires d'ouverture.
const stores = {
type: "FeatureCollection",
features: [
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.9041, 52.3676] },
properties: {
id: 1,
name: "Amsterdam Central",
address: "Stationsplein 12, 1012 AB Amsterdam",
phone: "+31 20 123 4567",
hours: "Mon–Sat 09:00–20:00"
}
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [4.4777, 51.9244] },
properties: {
id: 2,
name: "Rotterdam Lijnbaan",
address: "Lijnbaan 10, 3012 EL Rotterdam",
phone: "+31 10 987 6543",
hours: "Mon–Sat 09:00–21:00"
}
},
{
type: "Feature",
geometry: { type: "Point", coordinates: [5.1214, 52.0907] },
properties: {
id: 3,
name: "Utrecht Centrum",
address: "Oudegracht 45, 3511 AB Utrecht",
phone: "+31 30 555 1234",
hours: "Mon–Sun 10:00–19:00"
}
}
]
};
En production, vous récupéreriez ces données depuis un endpoint API ou un CMS. La structure reste la même, seule l'origine des données change.
Étape 3 : Afficher la carte et ajouter le regroupement
Initialisez la carte, ajoutez les données de magasins comme source GeoJSON avec le regroupement activé, et dessinez les cercles de cluster et la couche d'épingles individuelles. Le regroupement Mapbox GL JS est intégré dans la définition de la source, aucun plugin requis.
const map = new mapmetricsgl.Map({
container: 'map',
style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=YOUR_API_KEY',
center: [5.2913, 52.1326], // Centre of the Netherlands
zoom: 7
});
map.on('load', () => {
// Add GeoJSON source with clustering
map.addSource('stores', {
type: 'geojson',
data: stores,
cluster: true,
clusterMaxZoom: 12,
clusterRadius: 50
});
// Cluster circles
map.addLayer({
id: 'clusters',
type: 'circle',
source: 'stores',
filter: ['has', 'point_count'],
paint: {
'circle-color': '#3B82F6',
'circle-radius': ['step', ['get', 'point_count'], 20, 10, 28, 30, 36]
}
});
// Cluster count labels
map.addLayer({
id: 'cluster-count',
type: 'symbol',
source: 'stores',
filter: ['has', 'point_count'],
layout: {
'text-field': '{point_count_abbreviated}',
'text-size': 13
},
paint: { 'text-color': '#ffffff' }
});
// Individual store pins
map.addLayer({
id: 'unclustered-point',
type: 'circle',
source: 'stores',
filter: ['!', ['has', 'point_count']],
paint: {
'circle-color': '#EF4444',
'circle-radius': 8,
'circle-stroke-width': 2,
'circle-stroke-color': '#ffffff'
}
});
});
Cliquer sur un cluster zoome pour révéler les magasins individuels. Cliquer sur une épingle individuelle ouvre une popup.
Étape 4 : Connecter les popups et le panneau de liste
Quand un utilisateur clique sur une épingle de magasin, affichez une popup sur la carte et surlignez la carte correspondante dans le panneau de liste. Les deux interactions doivent être bidirectionnelles : cliquer sur une carte de liste doit également faire voler la carte vers ce magasin.
// Click unclustered store → open popup + highlight list card
map.on('click', 'unclustered-point', (e) => {
const { coordinates } = e.features[0].geometry;
const { name, address, phone, hours, id } = e.features[0].properties;
new mapmetricsgl.Popup()
.setLngLat(coordinates)
.setHTML(`
<strong>${name}</strong>
<p>${address}</p>
<p>${phone}</p>
<p>${hours}</p>
`)
.addTo(map);
highlightCard(id);
});
// Click cluster → zoom in
map.on('click', 'clusters', (e) => {
const features = map.queryRenderedFeatures(e.point, { layers: ['clusters'] });
const clusterId = features[0].properties.cluster_id;
map.getSource('stores').getClusterExpansionZoom(clusterId, (err, zoom) => {
if (err) return;
map.easeTo({ center: features[0].geometry.coordinates, zoom });
});
});
function highlightCard(id) {
document.querySelectorAll('.store-card').forEach(card => {
card.classList.toggle('active', card.dataset.id === String(id));
});
}
// Build list panel from store data
function buildListPanel() {
const list = document.getElementById('store-list');
stores.features.forEach(({ properties, geometry }) => {
const card = document.createElement('div');
card.className = 'store-card';
card.dataset.id = properties.id;
card.innerHTML = `
<strong>${properties.name}</strong>
<p>${properties.address}</p>
<small>${properties.hours}</small>
`;
card.addEventListener('click', () => {
map.flyTo({ center: geometry.coordinates, zoom: 14 });
highlightCard(properties.id);
});
list.appendChild(card);
});
}
Étape 5 : Ajouter la recherche d'adresse avec l'API de géocodage
La barre de recherche prend la localisation saisie par l'utilisateur, la géocode via l'API de géocodage, fait voler la carte vers ce point, et re-trie le panneau de liste par distance.
async function searchLocation(query) {
const url = new URL('https://api.mapatlas.eu/geocoding/v1/search');
url.searchParams.set('text', query);
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
const data = await res.json();
if (!data.features.length) {
alert('Address not found. Try a city or postcode.');
return;
}
const [lng, lat] = data.features[0].geometry.coordinates;
// Fly map to searched location
map.flyTo({ center: [lng, lat], zoom: 10 });
// Sort list by distance from searched point
const sorted = [...stores.features].sort((a, b) => {
const distA = haversine(lat, lng, a.geometry.coordinates[1], a.geometry.coordinates[0]);
const distB = haversine(lat, lng, b.geometry.coordinates[1], b.geometry.coordinates[0]);
return distA - distB;
});
document.getElementById('store-list').innerHTML = '';
sorted.forEach(feature => {
// Re-render each card (reuse buildListPanel logic)
});
}
function haversine(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a =
Math.sin(dLat / 2) ** 2 +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
document.getElementById('search-btn').addEventListener('click', () => {
const query = document.getElementById('search-input').value.trim();
if (query) searchLocation(query);
});
Étape 6 : Mise en page adaptée aux mobiles
Un localisateur de magasin sur mobile doit s'empiler verticalement, carte en haut et liste en dessous, plutôt que côte à côte. Vingt lignes de CSS gèrent cela avec un seul point de rupture de requête média.
#locator-wrapper {
display: flex;
height: 600px;
gap: 0;
}
#store-list {
width: 300px;
overflow-y: auto;
border-right: 1px solid #e5e7eb;
padding: 12px;
}
#map {
flex: 1;
}
.store-card {
padding: 12px;
border-radius: 8px;
cursor: pointer;
margin-bottom: 8px;
border: 2px solid transparent;
transition: border-color 0.2s;
}
.store-card.active {
border-color: #3B82F6;
background: #EFF6FF;
}
@media (max-width: 640px) {
#locator-wrapper {
flex-direction: column;
height: auto;
}
#store-list {
width: 100%;
border-right: none;
border-top: 1px solid #e5e7eb;
height: 280px;
}
#map {
height: 350px;
}
}
Comparaison de facturation et RGPD avec Google Maps
Si vous avez utilisé Google Maps sur un site de vente au détail et vous demandez pourquoi la facture de ce mois est plus élevée que prévu, vous n'êtes pas seul. L'API Maps JavaScript facture par chargement de carte. L'API Places facture par session d'autocomplétion et par requête de géocodage. Ces coûts s'accumulent vite. Un site réalisant 50 000 visites par mois, chacune chargeant la page de localisateur une fois, dépense environ 140 €/mois en chargements de carte seuls, avant un seul appel de géocodage.
MapAtlas utilise des forfaits mensuels fixes. Il n'y a pas de charge par chargement ou par requête qui monte sans avertissement. Vous pouvez lire le détail complet dans Tarification de l'API Google Maps en 2026 : le vrai coût et la comparaison MapAtlas vs. Google Maps.
Pour les développeurs de l'UE, l'angle RGPD est également important. Google Maps achemine les données via une infrastructure américaine. MapAtlas est hébergé dans l'UE, certifié ISO 27001, et traite toutes les requêtes dans l'UE. Pour les entreprises de détail qui gèrent déjà soigneusement le consentement des clients, utiliser un fournisseur de cartographie natif de l'UE supprime un transfert tiers supplémentaire de votre politique de confidentialité.
Tout assembler
Le localisateur de magasin complet, structure HTML, mise en page CSS, initialisation de carte, regroupement, gestion des popups, panneau de liste, recherche et tri par distance, tient confortablement dans un seul fichier. La structure ressemble à ceci :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Store Locator</title>
<link rel="stylesheet"
href="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css" />
<script src="https://unpkg.com/@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.js"></script>
<style>
/* paste the CSS from Step 6 here */
</style>
</head>
<body>
<div id="search-bar">
<input id="search-input" type="text" placeholder="Enter your city or postcode…" />
<button id="search-btn">Search</button>
</div>
<div id="locator-wrapper">
<div id="store-list"></div>
<div id="map"></div>
</div>
<script>
// paste store data, map init, clustering, popup, list panel, and search from Steps 2–5
</script>
</body>
</html>
Le résultat est un localisateur de magasin prêt pour la production sans dépendances externes au-delà du SDK MapAtlas. Il n'y a pas d'étape de build, pas de framework, et pas de surprises de facturation continues.
Si vous avez besoin d'ajouter le routage, "obtenir un itinéraire depuis ma position jusqu'à ce magasin", l'API Routing prend les coordonnées de l'utilisateur et celles du magasin et renvoie un itinéraire complet virage par virage que vous pouvez dessiner sur la carte comme couche de ligne. Le tutoriel Comment ajouter des cartes interactives à votre site web couvre cette prochaine étape en détail.
Prochaines étapes
- Créez une clé API MapAtlas gratuite, sans carte de crédit requise
- Parcourez la documentation de l'API Maps pour le regroupement, le style personnalisé et les options de couches
- Explorez l'API de géocodage pour la recherche de codes postaux, le géocodage inverse et l'autocomplétion d'adresses
Questions fréquentes
Puis-je créer un localisateur de magasin sans Google Maps ?
Oui. MapAtlas fournit une API Maps compatible Mapbox GL JS et une API de géocodage qui couvrent toutes les fonctionnalités dont un localisateur de magasin a besoin : carte interactive, recherche d'adresse, regroupement de marqueurs et popups, sans facturation par chargement et avec une conformité RGPD complète.
Combien coûte l'exploitation d'un localisateur de magasin sur MapAtlas par rapport à Google Maps ?
MapAtlas est environ 75 % moins cher que Google Maps pour une utilisation équivalente. Google Maps facture par chargement de carte et par requête de géocodage, ce qui s'accumule rapidement sur un site de vente au détail très fréquenté. MapAtlas utilise des forfaits mensuels fixes sans surprises par requête.
MapAtlas fonctionne-t-il sur WordPress et Shopify ?
Oui. Parce que MapAtlas est du JavaScript pur sans dépendance de framework, vous pouvez l'intégrer dans un bloc HTML personnalisé WordPress, une section de thème Shopify, ou n'importe quel CMS qui vous permet d'ajouter une balise script et une div.

