すべての小売ブランド、フランチャイズ、サービスビジネスは最終的にストアロケーターを必要とする。それはすでに購入したいと思っている顧客が訪れるページで、どの店舗に入るかを知るためだけに必要だ。そのページを間違えると実際のコンバージョンを失う。正しく作ることは多くの開発者が予想するよりも簡単だ。
標準的なアプローチはGoogle Mapsだったが、それはスケールで問題になるコスト構造を伴う。50,000人の月間訪問者を持つ忙しい小売サイトでマップページをロードすると、一晩でマップロードとジオコーディング料金で数百ユーロに達する可能性がある。本番環境ではもはや無料ティアがなく、料金は不透明で、USベースのマッピングサービスを使用するEUビジネスのGDPRの状況はコンプライアンスの諸費用を加える。
このチュートリアルでは、MapAtlas Maps APIとジオコーディングAPIを使った完全なストアロケーターを構築する。インタラクティブマップ、住所検索、同期リストパネル、マーカークラスタリング、クリックして詳細ポップアップ。フレームワーク不要。完全な動作例はHTMLとJavaScriptの約80行に収まる。この記事を読んだ当日の午後にWordPressのカスタムHTMLブロック、Shopifyセクション、または任意のCMSに組み込める。
最終的に以下が得られる:
- ストアネットワークを中心にしたベクターマップのレンダリング
- 低ズームでクラスタリング付きのJSONデータ配列からロードされたマーカー
- ジオコーディングAPIが動かす住所検索バー
- マップクリックでハイライトされる同期リストパネル
- モバイル対応の2カラムレイアウト
ストアロケーターに実際に必要なもの
一行のコードを書く前に、要件を正確に把握することが役立つ。動作するストアロケーターには4つの動く部品がある:
- タイルをレンダリングし、パンとズームを受け付け、マーカーを表示するマップ。
- ユーザーが入力した住所を座標にジオコードし、マップを再センタリングする検索入力。
- 各ストアの場所をプロットし、低ズームで近くのピンをクラスタリングし、クリックで詳細ポップアップを開くマーカーレイヤー。
- 検索した場所からの距離でソートされたストアを表示し、アクティブなストアをハイライトし、マップと同期してスクロールするリストパネル。
それだけだ。案内、営業時間、在庫など他のすべての機能は、これら4つの上に積み重ねられた強化機能だ。まずコアを構築しよう。
ステップ1:MapAtlas SDKをロードする
MapAtlas Maps APIはMapbox GL JSインターフェースと互換性があるので、Mapbox GL JSのチュートリアルやプラグインは直接使用できる。ページの<head>にCDNリンクを追加する:
<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>
npmを使用している場合:
npm install @mapmetrics/mapmetrics-gl
portal.mapmetrics.org/signupで無料APIキーを取得する。1つのキーがマップタイル、ジオコーディング、ルーティングをカバーし、別の認証情報は不要だ。
ステップ2:ストアデータを定義する
ストアデータはGeoJSON FeatureCollectionだ。各フィーチャーはストアの座標とポップアップに必要なプロパティを持つ:名前、住所、電話番号、営業時間。
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"
}
}
]
};
本番環境ではこの配列はAPIエンドポイントまたはCMSから取得する。構造は同じで、データのどこから来るかが違うだけだ。
ステップ3:マップをレンダリングしてクラスタリングを追加する
マップを初期化し、ストアデータをクラスタリング有効のGeoJSONソースとして追加し、クラスター円と個々のピンレイヤーを描画する。Mapbox GL JSのクラスタリングはソース定義に組み込まれており、プラグインは不要だ。
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'
}
});
});
クラスターをクリックすると個々のストアが表示されるようにズームインする。クラスター化されていないピンをクリックするとポップアップが開く。
ステップ4:ポップアップとリストパネルを接続する
ユーザーがストアピンをクリックすると、マップにポップアップを表示し、リストパネルの対応するカードをハイライトする。両方のインタラクションが双方向であるべきで、リストカードをクリックするとマップがそのストアにフライするべきだ。
// 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);
});
}
ステップ5:ジオコーディングAPIで住所検索を追加する
検索バーはユーザーが入力した場所を取得し、ジオコーディングAPIでジオコードし、マップをその地点にフライし、リストパネルを距離順に並べ替える。
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);
});
ステップ6:モバイルレスポンシブレイアウト
モバイルのストアロケーターは縦に並ぶべきで、マップが上、リストが下、横並びではなく。20行のCSSが1つのメディアクエリブレークポイントでこれを処理する。
#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;
}
}
Google Mapsとの請求とGDPR比較
Google Mapsを小売サイトで実行していて、今月の請求書が予想より高くて不思議に思っているなら、あなただけではない。Maps JavaScript APIはマップロードごとに課金する。Places APIはオートコンプリートセッションとジオコーディングリクエストごとに課金する。これらのコストは急速に複合する。50,000訪問/月のサイトで、各訪問がストアロケーターページを1回ロードすると、単一のジオコーディングコール前にマップロードだけで約140ユーロ/月かかる。
MapAtlasはフラット月額プランを使用する。警告なしに急騰するロードごとまたはリクエストごとの料金はない。完全な内訳は2026年Google Maps API価格:真のコスト内訳とMapAtlas vs. Google Maps比較で読める。
EU開発者にとってGDPRの側面も重要だ。Google MapsはUSインフラを通じてデータをルーティングする。MapAtlasはEUホスト、ISO 27001認定で、EU内のすべてのリクエストを処理する。顧客の同意を慎重に管理している小売ビジネスにとって、EU固有のマッピングプロバイダーを使用することで、プライバシーポリシーからさらに1つのサードパーティ転送を削除できる。
まとめ
完全なストアロケーター、HTML構造、CSSレイアウト、マップ初期化、クラスタリング、ポップアップ処理、リストパネル、検索、距離ソートは、1つのファイルに快適に収まる。構造は次のようになる:
<!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>
結果は、MapAtlas SDK以外の外部依存性がゼロの本番環境に対応したストアロケーターだ。ビルドステップ、フレームワーク、継続的な請求の驚きがない。
ルーティングを追加する必要がある場合、「現在地からこのストアへの道案内を取得する」、ルーティングAPIはユーザーの座標とストアの座標を受け取り、マップ上にラインレイヤーとして描画できる完全なターンバイターンのルートを返す。ウェブサイトにインタラクティブマップを追加する方法チュートリアルがその次のステップを詳しく説明している。
次のステップ
- MapAtlas APIキーの無料サインアップ、クレジットカード不要
- クラスタリング、カスタムスタイリング、レイヤーオプションについてはマップAPIドキュメントを参照する
- 郵便番号検索、逆ジオコーディング、住所オートコンプリートについてはジオコーディングAPIを探る
よくある質問
Google Mapsなしでストアロケーターを構築できますか?
はい。MapAtlasはMapbox GL JS互換のMaps APIとジオコーディングAPIを提供し、ストアロケーターに必要なすべての機能、インタラクティブマップ、住所検索、マーカークラスタリング、ポップアップを、ロードごとの課金なしとGDPR完全コンプライアンスで提供します。
MapAtlasとGoogle Mapsのストアロケーターのコストはどれくらい違いますか?
MapAtlasは同等の使用量でGoogle Mapsより約75%安価です。Google Mapsはマップロードとジオコーディングリクエストごとに課金し、忙しい小売サイトでは急速に積み上がります。MapAtlasはリクエストごとの予期しない料金なしのフラット月額プランを使用しています。
MapAtlasはWordPressとShopifyで動作しますか?
はい。MapAtlasはフレームワーク依存性のない純粋なJavaScriptなので、WordPressのカスタムHTMLブロック、Shopifyテーマセクション、またはスクリプトタグとdivを追加できるCMSに埋め込むことができます。
よくある質問
Google Mapsなしでストアロケーターを構築できますか?
はい。MapAtlasはMapbox GL JS互換のMaps APIとジオコーディングAPIを提供し、ストアロケーターに必要なすべての機能、インタラクティブマップ、住所検索、マーカークラスタリング、ポップアップを、ロードごとの課金なしとGDPR完全コンプライアンスで提供します。
MapAtlasとGoogle Mapsのストアロケーターのコストはどれくらい違いますか?
MapAtlasは同等の使用量でGoogle Mapsより約75%安価です。Google Mapsはマップロードとジオコーディングリクエストごとに課金し、忙しい小売サイトでは急速に積み上がります。MapAtlasはリクエストごとの予期しない料金なしのフラット月額プランを使用しています。
MapAtlasはWordPressとShopifyで動作しますか?
はい。MapAtlasはフレームワーク依存性のない純粋なJavaScriptなので、WordPressのカスタムHTMLブロック、Shopifyテーマセクション、またはスクリプトタグとdivを追加できるCMSに埋め込むことができます。

