Google Maps API で社内向けの情報集約マップを作る連載の第2回です。前回(第1回)では、APIキーとMap IDを用意して、Nuxt 4 で東京駅を中心にした地図を1枚表示しました。今回はその地図の上に、店舗・物件・現場・点検の4カテゴリのサンプル地点20件を、色分けしたピン(マーカー)で表示します。
「地図にピンを立てるだけなら簡単そうだけど、古いやり方と新しいやり方があると聞いた。どちらで作ればいいの?」
結論として、新しく作るなら Advanced Markers(AdvancedMarkerElement と PinElement)一択です。以前の google.maps.Marker は2024年2月に非推奨になっています。この回では Advanced Markers でカテゴリごとに色と文字を変えたピンを立て、今後の回で使う「地点データの型」も決めます。
Google Maps のマーカーは「Advanced Markers」が現在の標準
📰 出典:Google Maps Platform「Deprecations」
公式の非推奨一覧では、google.maps.Marker は Maps JavaScript API のバージョン3.56(2024年2月)で非推奨とされています。執筆時点(2026年9月)では提供終了の予定は発表されておらず、終了する場合は少なくとも12か月前に告知するとされていますが、推奨は AdvancedMarkerElement です。
| 項目 | 旧 Marker | Advanced Markers |
|---|---|---|
| クラス | google.maps.Marker | google.maps.marker.AdvancedMarkerElement |
| 状態 | 非推奨(2024年2月〜) | 推奨 |
| 見た目の変更 | アイコン画像を差し替え | PinElement で色・文字を指定、または任意のHTML |
| Map ID | 不要 | 必須(開発中は DEMO_MAP_ID で可) |
📰 出典:Google Maps Platform「Advanced Markers overview」
Advanced Markers は Map ID が必須です。第1回の MapView.vue で mapId を渡していたのは、この回のためです。
最新の書き方で変わった点
@types/google.maps 3 系の型定義を確認すると、Advanced Markers まわりでもいくつかの書き方が非推奨になっています。古い解説記事のコードをそのまま使うと警告の原因になるので、次の表で押さえておきましょう。
| 旧い書き方(非推奨) | 現在の書き方 |
|---|---|
content: pin.element | PinElement 自体が HTML 要素なので marker.append(pin) |
new PinElement({ glyph: '店' }) | glyphText: '店'(画像なら glyphSrc) |
marker.addListener('click', …) | marker.addEventListener('gmp-click', …)(第4回で使用) |
📰 出典:Google Maps Platform「Basic marker customization」
公式のカスタマイズ例も PinElement を作って marker.append(pin) で付ける形になっています。
実装:地点データの型とサンプル20件を用意する
この回で追加・変更したファイルは次のとおりです。
| ファイル | 役割 |
|---|---|
app/types/point.ts | 地点の型とカテゴリの定義(色・表示名) |
app/data/sample-points.ts | サンプル20地点(第3回でDBに置き換え) |
app/composables/useMarkers.ts | 地点データからマーカーを作る |
app/components/CategoryLegend.vue | カテゴリの凡例 |
app/pages/index.vue | 地図の準備ができたらマーカーを描く |
手順1:地点の型とカテゴリの定義
app/types/point.ts
export const CATEGORIES = ['store', 'property', 'site', 'inspection'] as const
export type Category = (typeof CATEGORIES)[number]
export interface Point {
id: number
name: string
category: Category
lat: number
lng: number
memo: string
updatedAt: string // ISO 8601
}
// カテゴリごとの表示名・ピンの色・ピン内の文字
export const CATEGORY_META: Record<
Category,
{ label: string; color: string; border: string; glyph: string }
> = {
store: { label: '店舗', color: '#1a73e8', border: '#0b4fb3', glyph: '店' },
property: { label: '物件', color: '#188038', border: '#0d5a26', glyph: '物' },
site: { label: '現場', color: '#e37400', border: '#a35300', glyph: '現' },
inspection: { label: '点検', color: '#a142f4', border: '#7127b5', glyph: '点' },
}
カテゴリを as const の配列で定義しておくと、型(Category)・凡例の並び順・次回のサーバー側の入力チェックで同じ定義を使い回せます。カテゴリの色と文字を1か所にまとめておけば、「点検の色を変えたい」という要望にも1行の修正で対応できます。
色だけで区別すると、色の見分けがつきにくい方には判別しにくくなります。そのため、ピンの中に「店」「物」などの文字も入れています。
手順2:サンプルデータ
app/data/sample-points.ts(抜粋)
import type { Point } from '~/types/point'
// 第2回用のサンプル地点(東京駅周辺・架空のデータ)。第3回で DB に置き換える。
const updatedAt = '2026-08-01T09:00:00+09:00'
export const samplePoints: Point[] = [
{ id: 1, name: '丸の内店', category: 'store', lat: 35.6812, lng: 139.7644, memo: '', updatedAt },
{ id: 6, name: '京橋ビル(空室あり)', category: 'property', lat: 35.6770, lng: 139.7708, memo: '', updatedAt },
{ id: 11, name: '八重洲二丁目 改修工事', category: 'site', lat: 35.6788, lng: 139.7728, memo: '', updatedAt },
{ id: 16, name: '呉服橋 消火設備点検', category: 'inspection', lat: 35.6830, lng: 139.7715, memo: '', updatedAt },
// …各カテゴリ5件ずつ、計20件(全体はサンプルコード参照)
]
名称と座標は東京駅周辺に置いた架空のデータです。緯度経度は自分で決めた値なので、Google の API から取得したデータに関する保存期間の制限(第6回で解説)は関係しません。
手順3:マーカーを作る composable
app/composables/useMarkers.ts
import { CATEGORY_META, type Point } from '~/types/point'
export function useMarkers() {
const { load } = useGoogleMaps()
const markers = new Map<number, google.maps.marker.AdvancedMarkerElement>()
async function render(map: google.maps.Map, points: Point[]) {
const { AdvancedMarkerElement, PinElement } = await load('marker')
clear()
for (const point of points) {
const meta = CATEGORY_META[point.category]
// カテゴリ色のピン(PinElement は HTMLElement なのでそのまま子要素にする)
const pin = new PinElement({
background: meta.color,
borderColor: meta.border,
glyphText: meta.glyph,
glyphColor: '#fff',
})
const marker = new AdvancedMarkerElement({
map,
position: { lat: point.lat, lng: point.lng },
title: `${meta.label}: ${point.name}`, // ツールチップ兼スクリーンリーダー用
})
marker.append(pin)
markers.set(point.id, marker)
}
}
function clear() {
for (const marker of markers.values()) marker.map = null
markers.clear()
}
// コンポーネントが破棄されたらマーカーも地図から外す
onScopeDispose(clear)
return { render, clear, markers }
}
ポイントは3つです。
load('marker')で marker ライブラリを読み込みます。第1回のuseGoogleMapsを通すので、キー未設定時のエラー処理も共通です- マーカーは Vue の
refに入れず、プレーンなMap(id → マーカー)で管理します。第1回の地図インスタンスと同じく、Vue の変更監視で重くならないようにするためです。id で引けるようにしておくと、第4回の「クリックした地点の詳細表示」や第8回の「一覧と地図の連動」でそのまま使えます - マーカーを地図から外すには
marker.map = nullとします。描き直しの前とコンポーネント破棄時に必ず外し、古いピンが残らないようにします
title を指定すると、マウスを重ねたときのツールチップになり、スクリーンリーダー向けの読み上げテキストにもなります。
手順4:ページで地図の準備を待ってから描く
app/pages/index.vue(script 部分)
<script setup lang="ts">
import { samplePoints } from '~/data/sample-points'
const { render } = useMarkers()
async function onMapReady(map: google.maps.Map) {
await render(map, samplePoints)
}
</script>
テンプレートでは、第1回の <ClientOnly> 内の <MapView> に @ready="onMapReady" を付け、上に凡例(<CategoryLegend />)を置いています。MapView は地図の作成が終わった時点で ready イベントを出すので、「地図がまだないのにマーカーを作ろうとする」順序の問題を避けられます。
動作確認の方法
.envにブラウザ用キーを設定し、npm run dev→ http://localhost:3000 を開く- 東京駅周辺に20個のピンが、青(店舗)・緑(物件)・オレンジ(現場)・紫(点検)で表示されることを確認
- ピンにマウスを重ねると「店舗: 丸の内店」のようなツールチップが出ることを確認
- DevTools のコンソールに、Map ID が無いことによる警告や、
Markerの非推奨警告が出ていないことを確認 npm run buildとnpm run typecheckが通ることを確認
筆者の環境では npm run build と npm run typecheck の成功、ビルド後のサーバーが返すHTMLに凡例(店舗・物件・現場・点検)が入っていることまでを確認しました。実際のピンの表示とコンソールの警告の有無は、執筆環境に本物のAPIキーがないため確認できていません。 ご自身のキーを .env に設定して確認してください。
つまずきやすい点
- Map ID を渡し忘れる:Advanced Markers は Map ID が無いと表示されません。
DEMO_MAP_IDは開発用なので、本番では自分のプロジェクトで Map ID を発行します - 同じ
PinElementを複数のマーカーに使い回す:要素は複製されず「移動」するため、前のマーカーが空になります。ピンはマーカーごとに作ります - 古い解説のコードを混ぜる:
pin.elementやglyph、addListener('click')は型定義上で非推奨です。TypeScript の型チェックを通しておくと、エディタ上で取り消し線や警告として気づけます - 件数が多い場合:20件ならこの方法で十分ですが、数千件になると1つずつ地図に載せるのは重くなります。第9回でクラスタリング(近いピンをまとめて表示)と表示範囲だけの読み込みを扱います
発注者向けメモ
- 既存システムの改修では「Marker → Advanced Markers 移行」が隠れ工数になりやすい。旧 Marker は今すぐ動かなくなるわけではありませんが、非推奨です。改修の見積りで地図部分に触れる場合は、移行を含めるかどうかを確認しておくと後で揉めません
- Map ID の発行は発注者のプロジェクトで。Map ID は Google Cloud のプロジェクトに紐づくため、第1回のキーと同じく発注者名義のプロジェクトで作るのが原則です
- ピンを立てること自体に追加のAPI費用はかからない構造です。地図の費用は主に地図の読み込み回数で決まり、マーカーは地図の中で描画されます。ただし「ピンの見た目を凝る(写真入り・吹き出し型など)」要望は、デザインと実装の工数に効いてきます
- カテゴリの種類と色は、業務の言葉で先に決めておく。後から増減すると、凡例・絞り込み(第8回)・CSV取り込み(第11回)にも影響します
開発会社への確認に使える質問例です。
- 「マーカーは Advanced Markers で実装しますか?既存の旧 Marker からの移行は見積りに入っていますか?」
- 「本番用の Map ID は、どのプロジェクトで誰が発行しますか?」
- 「色の見分けがつきにくい利用者にも区別できるよう、ピンに文字やアイコンを入れられますか?」
まとめと次回予告
この回では、非推奨の旧 Marker ではなく Advanced Markers を使い、カテゴリごとに色と文字を変えたピンを20件表示しました。PinElement は marker.append(pin) で付ける、文字は glyphText で指定する、マーカーは Vue のリアクティブにせず id で引ける Map で管理する、の3点が押さえどころです。
次回は「地点データをNuxt server API + SQLiteに保存する」です。今回ファイルに直書きしたサンプル地点を、Nuxt のサーバーAPIとSQLiteに移し、登録・更新・削除ができる土台を作ります。
この連載の記事一覧
この記事は連載「NuxtとGoogle Mapsで作る情報集約マップ」の1回です。連載のほかの回は次のとおりです(連載の一覧ページ)。
- 【NuxtとGoogle Mapsで作る情報集約マップ 第1回】APIキーとMap IDを用意して、Nuxt 4で地図を1枚表示する
- 【NuxtとGoogle Mapsで作る情報集約マップ 第2回】Advanced Markersでカテゴリ別の色付きピンを立てる(この記事)
- 【NuxtとGoogle Mapsで作る情報集約マップ 第3回】地点データをNuxt server API + SQLiteに保存する
- 【NuxtとGoogle Mapsで作る情報集約マップ 第4回】マーカーをクリックしてInfoWindowと詳細パネルを出す
- 【NuxtとGoogle Mapsで作る情報集約マップ 第5回】地図をクリックして新しい地点を登録する
- 【NuxtとGoogle Mapsで作る情報集約マップ 第6回】住所を入力して緯度経度に変換する(Geocoding API v4をサーバー経由で)
- 【NuxtとGoogle Mapsで作る情報集約マップ 第7回】施設名で検索して地点を取り込む(Places API (New))
- 【NuxtとGoogle Mapsで作る情報集約マップ 第8回】カテゴリで絞り込み、一覧と地図を連動させる
- 【NuxtとGoogle Mapsで作る情報集約マップ 第9回】マーカーが数千件でも重くしない:クラスタリングと表示範囲読み込み
- 【NuxtとGoogle Mapsで作る情報集約マップ 第10回】現在地から半径◯km/描いたエリア内の地点を探す
- 【NuxtとGoogle Mapsで作る情報集約マップ 第11回】CSVで既存の台帳を一括取り込みする
- 【NuxtとGoogle Mapsで作る情報集約マップ 第12回】本番公開前チェック:APIキー制限・クォータ・予算アラート・性能










コメント
コメント一覧 (1件)
[…] Maps API とNuxtで社内向けの情報集約マップを作る連載の第3回です。前回(第2回)では、ファイルに直書きしたサンプル20地点を、Advanced Markers […]