はじめに
本記事では、ZENRIN Maps API のウィジェットや図形を表示する際の「表示順序(z-index)」の制御方法について解説します。
地図上に複数の図形やウィジェット(例: 配達可能エリアのポリゴン、配達ルートのポリライン、配送先のマーカー、注文情報のポップアップ)を配置した場合、重要な情報を手前に表示するために z-index の設定が重要になります。
この記事でできること
- ZENRIN Maps APIで地図を描画
複数ウィジェットの重なり順序の制御 - Polygon(ポリゴン)、Polyline(ポリライン)、Marker(マーカー)、Popup(ポップアップ)の z-index 設定
- z-index を動的に変更できる UI の作成
APIキーの取得手順
ZENRIN Maps API を利用するには、事前に APIキーの取得が必要です。
現在、ZENRIN Maps API は 2か月間の無料トライアルが用意されており、期間中は主要な機能を実際にお試しいただけます。開発や評価の初期段階でも安心してご利用いただけます。
APIキーの取得方法については、以下の記事で詳しく解説されています。
初めての方は、まずこちらをご覧いただき、APIキーの発行と設定を行ってください。
公式リファレンス
- 地図描画 メインクラス(Map)
- ユーザーウィジェットのクラス(UserWidget)
- 楕円のクラス(Oval)
- ポリゴンのクラス(Polygon)
- ポリラインのクラス(Polyline)
- 吹き出しのクラス(Popup)
- マーカーのクラス(Marker)
- 基本サンプル - ウィジェットのz-indexを変更する
実装イメージ(例: 配達サービスの動線管理)
今回は例として「配達サービス」を想定し、以下の情報を地図上に配置して表示順序を制御します。
- 🏪 配達可能エリア(Polygon)
- 🛣️ 配達ルート(Polyline)
- 🏠 配送先(Marker)
- 📋 注文情報(Popup)
⚠️ 注意
図形クラス(Polygon / Polyline)とウィジェットクラス(Marker / Popup)間では、z-index の重なり順序は保証されません。
そのため同一クラス間での制御を前提としてください。
ファイル構成
project/
├── index.html # メインHTML
├── css/
│ └── zma_zindex.css # スタイルシート
└── js/
└── zma_zindex.js # JavaScript
サンプルコード
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>配達サービス動線管理のz-index制御デモ</title>
<script src="https://test-js.zmaps-api.com/zma_loader.js?key=[APIキー]&auth=referer"></script>
<link rel="stylesheet" href="css/zma_zindex.css">
</head>
<body>
<div id="ZMap">
<div class="control-panel">
<h3>🚚 配達サービス動線管理</h3>
<div class="scenario-info">
<strong>シナリオ:</strong> 飲食店の配達可能エリア、配達ルート、配送先、注文情報の表示順序を制御<br>
<strong>目的:</strong> 重要な情報ほど手前に表示し、効率的な配達管理を実現<br>
<strong>注意:</strong> 図形クラス(Polygon/Polyline)とウィジェットクラス(Marker/Popup)間では、z-indexの重なり順序は保証されません
</div>
<div class="widget-control area" data-widget="polygon">
<label>🏪 配達可能エリア (Polygon)</label>
<div class="z-index-buttons">
<button onclick="setPolygonZIndex(99)">99</button>
<button onclick="setPolygonZIndex(100)" class="active">100</button>
<button onclick="setPolygonZIndex(101)">101</button>
<button onclick="setPolygonZIndex(102)">102</button>
<button onclick="setPolygonZIndex(103)">103</button>
</div>
<div class="info-text">現在: 100 (背景情報)</div>
</div>
<div class="widget-control route" data-widget="polyline">
<label>🛣️ 配達ルート (Polyline)</label>
<div class="z-index-buttons">
<button onclick="setPolylineZIndex(99)">99</button>
<button onclick="setPolylineZIndex(100)">100</button>
<button onclick="setPolylineZIndex(101)" class="active">101</button>
<button onclick="setPolylineZIndex(102)">102</button>
<button onclick="setPolylineZIndex(103)">103</button>
</div>
<div class="info-text">現在: 101 (ルート情報)</div>
</div>
<div class="widget-control destination" data-widget="marker">
<label>🏠 配送先 (Marker)</label>
<div class="z-index-buttons">
<button onclick="setMarkerZIndex(99)">99</button>
<button onclick="setMarkerZIndex(100)">100</button>
<button onclick="setMarkerZIndex(101)">101</button>
<button onclick="setMarkerZIndex(102)" class="active">102</button>
<button onclick="setMarkerZIndex(103)">103</button>
</div>
<div class="info-text">現在: 102 (配送先)</div>
</div>
<div class="widget-control info" data-widget="popup">
<label>📋 注文情報 (Popup)</label>
<div class="z-index-buttons">
<button onclick="setPopupZIndex(99)">99</button>
<button onclick="setPopupZIndex(100)">100</button>
<button onclick="setPopupZIndex(101)">101</button>
<button onclick="setPopupZIndex(102)">102</button>
<button onclick="setPopupZIndex(103)" class="active">103</button>
</div>
<div class="info-text">現在: 103 (注文詳細)</div>
</div>
</div>
</div>
<script src="js/zma_zindex.js"></script>
</body>
</html>
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
#ZMap {
position: relative;
width: 100vw;
height: 100vh;
border: none;
}
.control-panel {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.95);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
z-index: 1000;
max-width: 350px;
}
.control-panel h3 {
margin: 0 0 10px 0;
font-size: 16px;
color: #2e7d32;
}
.widget-control {
margin-bottom: 10px;
padding: 8px;
background: #f5f5f5;
border-radius: 4px;
border-left: 4px solid #ccc;
}
.widget-control.area {
border-left-color: #4caf50;
background: #e8f5e8;
}
.widget-control.route {
border-left-color: #2196f3;
background: #e3f2fd;
}
.widget-control.destination {
border-left-color: #ff9800;
background: #fff3e0;
}
.widget-control.info {
border-left-color: #9c27b0;
background: #f3e5f5;
}
.widget-control label {
display: block;
font-weight: bold;
margin-bottom: 5px;
font-size: 12px;
}
.z-index-buttons {
display: flex;
gap: 5px;
flex-wrap: wrap;
}
.z-index-buttons button {
padding: 4px 8px;
font-size: 11px;
border: 1px solid #ccc;
background: white;
cursor: pointer;
border-radius: 3px;
}
.z-index-buttons button:hover {
background: #e0e0e0;
}
.z-index-buttons button.active {
background: #007bff;
color: white;
border-color: #007bff;
}
.info-text {
font-size: 11px;
color: #666;
margin-top: 5px;
}
.scenario-info {
font-size: 11px;
color: #444;
margin: 8px 0 12px 0;
line-height: 1.5;
background: #e8f5e8;
padding: 8px;
border-radius: 4px;
border: 1px solid #4caf50;
}
var map, polygon, polygonBase, polyline, polylineBase, markers = [], popups = [];
// 中心点の緯度経度(東京駅周辺)
const lat = 35.681406, lng = 139.749132;
ZMALoader.setOnLoad(function (mapOptions, error) {
if (error) {
console.error(error);
return;
}
// マップオプションを設定
mapOptions.center = new ZDC.LatLng(lat, lng);
mapOptions.zoom = 14;
// 地図を生成
map = new ZDC.Map(
document.getElementById('ZMap'),
mapOptions,
function() {
// Success callback
createDeliveryInfo();
},
function() {
console.error('Map creation failed');
}
);
});
function createDeliveryInfo() {
// 配達可能エリア (Polygon) - 飲食店の配達エリア
var deliveryArea = [];
deliveryArea.push(new ZDC.LatLng(35.6607694489887, 139.7419563985648));
deliveryArea.push(new ZDC.LatLng(35.6845229143682, 139.7286721190967));
deliveryArea.push(new ZDC.LatLng(35.7057395047171, 139.7473525622848));
deliveryArea.push(new ZDC.LatLng(35.7032096194981, 139.7793172849411));
deliveryArea.push(new ZDC.LatLng(35.6794617174330, 139.7926015644093));
deliveryArea.push(new ZDC.LatLng(35.6582381374606, 139.7739211212211));
deliveryArea.push(new ZDC.LatLng(35.6607694489887, 139.7419563985648));
// 配達エリアのベース(緑)
polygonBase = new ZDC.Polygon(deliveryArea, {
fill: '#4caf50',
strokeWidth: 1,
opacity: 0.3
});
map.addWidget(polygonBase);
polygonBase.setZIndex(99);
// 配達エリアのメイン(緑)
polygon = new ZDC.Polygon(deliveryArea, {
fill: '#4caf50',
strokeWidth: 2,
opacity: 0.6
});
map.addWidget(polygon);
polygon.setZIndex(100);
// 配達ルート (Polyline) - 最適な配達経路(polygon内に配置)
var deliveryRoutes = [];
deliveryRoutes.push(new ZDC.LatLng(35.67, 139.75)); // polygon内の点1
deliveryRoutes.push(new ZDC.LatLng(35.68, 139.77)); // polygon内の点2
deliveryRoutes.push(new ZDC.LatLng(35.69, 139.76)); // polygon内の点3
deliveryRoutes.push(new ZDC.LatLng(35.70, 139.75)); // polygon内の点4
deliveryRoutes.push(new ZDC.LatLng(35.67, 139.75)); // 始点に戻る
// 配達ルートのベース(青)
polylineBase = new ZDC.Polyline(deliveryRoutes, {
color: '#2196f3',
width: 6
});
map.addWidget(polylineBase);
polylineBase.setZIndex(100);
// 配達ルートのメイン(青)
polyline = new ZDC.Polyline(deliveryRoutes, {
color: '#2196f3',
width: 4
});
map.addWidget(polyline);
polyline.setZIndex(101);
// 配送先 (Marker) - polylineの各接続点に表示
var markerPositions = [
new ZDC.LatLng(35.67, 139.75), // 点1
new ZDC.LatLng(35.68, 139.77), // 点2
new ZDC.LatLng(35.70, 139.75) // 点3
];
// 各接続点にマーカーを作成
for (var i = 0; i < markerPositions.length; i++) {
var marker = new ZDC.Marker(markerPositions[i]);
map.addWidget(marker);
marker.setZIndex(102);
markers.push(marker); // 配列に追加
}
// 注文情報 (Popup) - 各接続点に表示(始点は除く)
var popupPositions = [
new ZDC.LatLng(35.68, 139.77), // 点2
new ZDC.LatLng(35.69, 139.76), // 点3
];
var popupContents = [
'<div style="padding: 8px; background: #f3e5f5; border: 2px solid #9c27b0; border-radius: 6px; color: #4a148c; font-size: 11px; min-width: 120px;"><strong>📋 注文1</strong><br>🍕 ピザ マルゲリータ<br>⏰ 18:30</div>',
'<div style="padding: 8px; background: #f3e5f5; border: 2px solid #9c27b0; border-radius: 6px; color: #4a148c; font-size: 11px; min-width: 120px;"><strong>📋 注文2</strong><br>🍜 ラーメン 醤油<br>⏰ 18:45</div>',
];
// 各接続点にポップアップを作成
var popupOffsets = [
new ZDC.Point(0, -20), // 点2のオフセット(上に20px)
new ZDC.Point(-90, 280), // 点3のオフセット(左に30px、上に20px)
];
for (var i = 0; i < popupPositions.length; i++) {
var popup = new ZDC.Popup(
popupPositions[i],
{
htmlSource: popupContents[i],
offset: popupOffsets[i],
propagation: true
}
);
map.addWidget(popup);
popup.setZIndex(103);
popups.push(popup); // 配列に追加
}
}
// z-index変更関数
function setPolygonZIndex(zIndex) {
polygon.setZIndex(zIndex);
updateButtonStates('polygon', zIndex);
}
function setPolylineZIndex(zIndex) {
polyline.setZIndex(zIndex);
updateButtonStates('polyline', zIndex);
}
function setMarkerZIndex(zIndex) {
// 全てのマーカーのz-indexを変更
for (var i = 0; i < markers.length; i++) {
markers[i].setZIndex(zIndex);
}
updateButtonStates('marker', zIndex);
}
function setPopupZIndex(zIndex) {
// 全てのポップアップのz-indexを変更
for (var i = 0; i < popups.length; i++) {
popups[i].setZIndex(zIndex);
}
updateButtonStates('popup', zIndex);
}
function updateButtonStates(widgetType, activeZIndex) {
// 指定されたウィジェットのコントロールのみを更新
const control = document.querySelector(`.widget-control[data-widget="${widgetType}"]`);
if (!control) return;
const buttons = control.querySelectorAll('.z-index-buttons button');
const infoText = control.querySelector('.info-text');
buttons.forEach(button => {
button.classList.remove('active');
if (parseInt(button.textContent) === activeZIndex) {
button.classList.add('active');
}
});
if (infoText) {
infoText.textContent = `現在: ${activeZIndex}`;
}
}
コードを実行した結果です。
※図形クラスのポリライン(Polyline)とウィジェットクラスの(Popup)が最上位

図形クラスのポリゴン(Polygon)とウィジェットクラスのマーカー(Marker)を最上位に変更

ステップ毎の解説
Step 1:HTML の基本構造と ZENRIN Maps API の読み込み
<script src="https://test-js.zmaps-api.com/zma_loader.js?key=YOUR_API_KEY&auth=referer"></script>
<link rel="stylesheet" href="css/zma_zindex.css" />
<script src="js/zma_zindex.js" defer></script>
-
zma_loader.jsで ZENRIN Maps API を読み込む。 -
css/zma_zindex.cssでマップとコントロールパネルのスタイルを設定。 -
js/zma_zindex.jsでマップ描画と z-index 制御のロジックを記述。 -
deferを付けることで DOM が読み込まれてからスクリプトを実行。
Step 2:地図描画用の HTML 要素とコントロールパネル
<div id="ZMap">
<div class="control-panel">
<h3>🚚 配達サービス動線管理</h3>
<div class="scenario-info">...</div>
<div class="widget-control area" data-widget="polygon">...</div>
<div class="widget-control route" data-widget="polyline">...</div>
<div class="widget-control destination" data-widget="marker">...</div>
<div class="widget-control info" data-widget="popup">...</div>
</div>
</div>
-
#ZMapが地図描画領域。 -
.control-panel内に、各ウィジェット(Polygon/Polyline/Marker/Popup)の z-index を変更するボタンと情報表示を配置。 -
data-widget属性で種類を判別。
Step 3:CSS でのスタイル設定
#ZMap {
position: relative;
width: 100vw;
height: 100vh;
}
.control-panel {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255, 255, 255, 0.95);
padding: 15px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
z-index: 1000;
max-width: 350px;
}
.widget-control.area { border-left-color: #4caf50; background: #e8f5e8; }
.widget-control.route { border-left-color: #2196f3; background: #e3f2fd; }
- マップは画面全体表示。
- コントロールパネルは地図上に重ねる形で固定。
-
.widget-controlごとに色分けして視覚的に識別。 - ボタンには
.activeで現在の z-index を強調。
Step 4:JavaScript:地図の初期化
const lat = 35.681406, lng = 139.767132;
ZMALoader.setOnLoad(function(mapOptions, error) {
if (error) {
console.error(error);
return;
}
mapOptions.center = new ZDC.LatLng(lat, lng);
mapOptions.zoom = 14;
map = new ZDC.Map(document.getElementById('ZMap'), mapOptions, function () {
createDeliveryInfo();
});
});
- 東京駅付近を地図の中心に設定。
- ズームレベル 14 で初期表示。
-
createDeliveryInfo()を呼んで Polygon, Polyline, Marker, Popup を作成。
Step 5:配達可能エリア(Polygon)の追加
var deliveryArea = [
deliveryArea.push(new ZDC.LatLng(35.6607694489887, 139.7419563985648));
...
];
polygonBase = new ZDC.Polygon(deliveryArea, { fill: '#4caf50', strokeWidth: 1, opacity: 0.3 });
map.addWidget(polygonBase);
polygonBase.setZIndex(99);
polygon = new ZDC.Polygon(deliveryArea, { fill: '#4caf50', strokeWidth: 2, opacity: 0.6 });
map.addWidget(polygon);
polygon.setZIndex(100);
- 背景情報として
polygonBase(薄い色、Z=99)。 - 手前に表示する主要 Polygon
polygon(濃い色、Z=100)。 -
setZIndex()で重なり順序を制御。
Step 6:配達ルート(Polyline)の追加
var deliveryRoutes = [
deliveryRoutes.push(new ZDC.LatLng(35.67, 139.75)); // polygon内の点1
...
];
polylineBase = new ZDC.Polyline(deliveryRoutes, { color: '#2196f3', width: 6 });
map.addWidget(polylineBase);
polylineBase.setZIndex(100);
polyline = new ZDC.Polyline(deliveryRoutes, { color: '#2196f3', width: 4 });
map.addWidget(polyline);
polyline.setZIndex(101);
- 太線 Polyline を下に敷き、細線 Polyline を上に表示。
- ルートの視認性と前後関係を z-index で調整。
Step 7:配送先(Marker)の追加
var markerPositions = [
new ZDC.LatLng(35.67, 139.75), // 点1
...
];
for (var i = 0; i < markerPositions.length; i++) {
var marker = new ZDC.Marker(markerPositions[i]);
map.addWidget(marker);
marker.setZIndex(102);
markers.push(marker); // 配列に追加
}
- 配送先を地図上にマーカーで表示。
- Polygon/Polyline より前面に表示するため Z=102。
Step 8:注文情報(Popup)の追加
var popupPositions = [
new ZDC.LatLng(35.68, 139.77), // 点2
new ZDC.LatLng(35.69, 139.76), // 点3
];
var popupContents = [
'<div style="padding: 8px; background: #f3e5f5; border: 2px solid #9c27b0; border-radius: 6px; color: #4a148c; font-size: 11px; min-width: 120px;"><strong>📋 注文1</strong><br>🍕 ピザ マルゲリータ<br>⏰ 18:30</div>',
'<div style="padding: 8px; background: #f3e5f5; border: 2px solid #9c27b0; border-radius: 6px; color: #4a148c; font-size: 11px; min-width: 120px;"><strong>📋 注文2</strong><br>🍜 ラーメン 醤油<br>⏰ 18:45</div>',
];
// 各接続点にポップアップを作成
var popupOffsets = [
new ZDC.Point(0, -20), // 点2のオフセット(上に20px)
new ZDC.Point(-90, 280), // 点3のオフセット(左に30px、上に20px)
];
for (var i = 0; i < popupPositions.length; i++) {
var popup = new ZDC.Popup(
popupPositions[i],
{
htmlSource: popupContents[i],
offset: popupOffsets[i],
propagation: true
}
);
map.addWidget(popup);
popup.setZIndex(103);
popups.push(popup); // 配列に追加
}
- 注文情報は Popup で表示。
- 最前面に配置(Z=103)。
- 配置位置やオフセットも調整可能。
Step 9:z-index を変更する関数
function setPolygonZIndex(zIndex) { polygon.setZIndex(zIndex); updateButtonStates('polygon', zIndex); }
function setPolylineZIndex(zIndex) { polyline.setZIndex(zIndex); updateButtonStates('polyline', zIndex); }
function setMarkerZIndex(zIndex) { marker.setZIndex(zIndex); updateButtonStates('marker', zIndex); }
function setPopupZIndex(zIndex) { popups.forEach(p => p.setZIndex(zIndex)); updateButtonStates('popup', zIndex); }
- 各ウィジェットの z-index を変更。
- ボタンのアクティブ状態も同時に更新。
Step 10:ボタン状態の更新
function updateButtonStates(widgetType, activeZIndex) {
const control = document.querySelector(`.widget-control[data-widget="${widgetType}"]`);
const buttons = control.querySelectorAll('.z-index-buttons button');
const infoText = control.querySelector('.info-text');
buttons.forEach(button => {
button.classList.remove('active');
if (parseInt(button.textContent) === activeZIndex) button.classList.add('active');
});
if (infoText) infoText.textContent = `現在: ${activeZIndex}`;
}
- ボタン UI を操作して、どの z-index が選択されているかを反映。
- ユーザーに現在の表示順序をわかりやすく提示。
おわりに
今回のサンプルでは、Polygon(ポリゴン)、Polyline(ポリライン)、Marker(マーカー)、Popup(ポップアップ) という複数の地図ウィジェットを z-index で重なり順を制御 する方法を紹介しました。
ポイントは以下の通りです:
- 情報の重要度に応じて表示順序を整理
- コントロールパネルのボタン操作で即座に変更可能
- 背景情報と前面情報を視覚的に区別して管理
この手法は、配達管理だけでなく、災害対応マップやイベント会場マップなど、多層情報を扱う地図アプリ でも応用できます。
特に、複数の情報レイヤーをユーザーが自由に入れ替えて確認できる UI を作る場合に便利です。
地図上の情報を より直感的で効率的に見せる工夫 の第一歩として、今回の z-index 制御の考え方を参考にしてみてください。