今さらなのですが、API を利用して Google Maps Platform で地図を表示しているページでふとブラウザのコンソールをみてみると、こんなエラーが。
Starting February 21, 2024, google.maps.Marker will no longer be available. Instead, use google.maps.marker.AdvancedMarkerElement. Find more information about the discontinuation at https://developers.google.com/maps/deprecations.
もしくは、以下の警告が表示される場合もあります。
As of February 21st, 2024, google.maps.Marker is deprecated. Please use google.maps.marker.AdvancedMarkerElement instead. At this time, google.maps.Marker is not scheduled to be discontinued, but google.maps.marker.AdvancedMarkerElement is recommended over google.maps.Marker. While google.maps.Marker will continue to receive bug fixes for any major regressions, existing bugs in google.maps.Marker will not be addressed. At least 12 months notice will be given before support is discontinued. Please see https://developers.google.com/maps/deprecations for additional details and https://developers.google.com/maps/documentation/javascript/advanced-markers/migration for the migration guide.
なるほど、google.maps.Marker
はいずれ廃止されるから google.maps.marker.AdvancedMarkerElement
にさっさと移行しなさいと。
google.maps.Marker
というのは、マップの対象地点にこのようなピンを表示するものです。
Google Maps API の読み込み
Google マップ Google Maps Platform の JavaScript API の読み込みもいつの間にか増えており、推奨方法が変わってました。
従来の読み込み方法
<script async
src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap">
</script>
Dynamic Library Import (現在の推奨方法)
key
(APIキーの指定)と v
(バージョン指定) のパラメータは必須のようです。
あとは必要に応じて Bootstrap パラメータを指定できます。
<script>
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "YOUR_API_KEY",
v: "weekly",
// Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
// Add other bootstrap parameters as needed, using camel case.
});
</script>
参考: Dynamic Library Import を使用する
npmパッケージを使用する場合
npm パッケージをインストールして読み込む方法もあります。
まずは npm で @googlemaps/js-api-loader というローダーをインストールします。
npm install @googlemaps/js-api-loader
これでプロジェクト内でローダーを読み込んで使用できるようになります。
import { Loader } from "@googlemaps/js-api-loader";
const loader = new Loader({
apiKey: "YOUR_API_KEY",
version: "weekly",
...additionalOptions,
});
loader.load().then(async () => {
const { Map } = await google.maps.importLibrary("maps");
map = new Map(document.getElementById("map"), {
center: { lat: -34.397, lng: 150.644 },
zoom: 8,
});
});
参考: NPM js-api-loader パッケージを使用する
採用した読み込み方法(暫定)
API の読み込みは、先述の3種類の方法がありますが、まだドキュメントが新旧ちぐはぐな状態のようで、ローダの移行ガイドには上記の「従来の読み込み方法」が推奨になっていたり、なかなか情報がとっ散らかっていて翻弄されます。
参考: <script> タグを使用するインライン読み込み(推奨)
Google Maps API は、「DigiPress Ex – Blocks」プラグインのマップブロックで利用しており、プロダクトとしてはフロントエンド用の JavaScript にて script タグを挿入して動的に API を読み込んでいます。
参考: 別の JavaScript ファイルからの動的読み込み
しかし、やり方が悪いのか Dynamic Library Import の方法では地図がグレーアウトしてしまい、うまく表示できないため、従来の script タグにバージョンと新しいマーカーを使用するためのライブラリ指定のパラメータ( &v=weekly&libraries=marker
)を追加することにしました。
const loadMapsAPI = ( apiKey ) => {
return new Promise( ( resolve, reject ) => {
const script = document.createElement( 'script' );
script.async = true;
script.src = `https://maps.googleapis.com/maps/api/js?key=${ apiKey }&callback=Function.prototype&v=weekly&libraries=marker`;
script.onload = resolve;
script.onerror = reject;
document.head.appendChild( script );
} );
}
高度なマーカーとやらに移行してみる
従来のマーカーは、google.maps.Marker
を呼び出していましたが、改めてドキュメントを確認してみると、新しいマーカー(高度なマーカー というらしい)は google.maps.marker.AdvancedMarkerView
を指定しなさいとあります。
また、高度なマーカーを使用するには、マップの生成時にマップID( mapId
)の指定が必須になったようなので、忘れずに指定しておきます。
const canvas = document.getElementById( 'map' );
const mapOptions = {
mapId: 'YOUR_MAP_ID', // マップID
zoom: 14,
center: { lat: -34.397, lng: 150.644 },
}
const map = new google.maps.Map( canvas, mapOptions );
早速、google.maps.Marker
を google.maps.marker.AdvancedMarkerView
に変えて実行してみます。
const initMap = () => {
const canvas = document.getElementById( 'map' );
const mapOptions = {
mapId: "4504f8b37365c3d0",
zoom: 14,
center: { lat: -34.397, lng: 150.644 },
}
// マップ生成
const map = new google.maps.Map( canvas, mapOptions );
// マーカー生成
const marker = new google.maps.marker.AdvancedMarkerView({
map,
position: { lat: 37.4239163, lng: -122.0947209 },
});
}
window.initMap = initMap;
しかし、実際に AdvancedMarkerView
を呼び出してみると、そんなコンストラクタはないと怒られます。
TypeError: google.maps.marker.AdvancedMarkerView is not a constructor
確かに、google.maps.Marker
(従来のマーカー)を呼び出した場合に表示されたエラーでは、AdvancedMarkerView
ではなく AdvancedMarkerElement
を使いなさい、と言われていました。
Starting February 21, 2024, google.maps.Marker will no longer be available. Instead, use google.maps.marker.AdvancedMarkerElement. Find more information about the discontinuation at https://developers.google.com/maps/deprecations.
Google のマーカーのドキュメントでは、AdvancedMarkerView
ばかりなので、AdvancedMarkerElement
とは別物なのかと思っていましたが、どうやら実際は AdvancedMarkerElement
のみが存在しているので、ドキュメントのコードはすべて AdvancedMarkerElement
に置換して解釈します。
公式ドキュメントの情報は新旧混在していたり、最新の状態でなかったり、欲しい情報があちこちに散らばっていたりと盛大にとっ散らかりすぎていて、本当に困ります…。
高度なマーカーのオプション
高度なマーカーは、google.maps.Marker
から google.maps.marker.AdvancedMarkerElement
に変えれば良いだけなのかと思いきや、かなり仕様が刷新されており後方互換がないため、従来の Marker
クラスのマーカーオプション(パラメータ)のままでは使用できません。
マーカーオプションは、map
, title
, position
くらいが共通しているくらいで、あとのパラメータはほぼ使えません。
参考: MarkerOptions インターフェース (従来のマーカー)
マーカーのアニメーションに至っては、高度なマーカーではアニメーション定数など存在せず、CSS で一から自作したキーフレームアニメーションを作成し、そのセレクタを生成したマーカー要素の class に追加する必要があります(後述)。
また、デフォルトのマーカーを予め用意されているシンボルで表示するシンボル定数もありません(シンボル機能自体がない)。
デフォルトのマーカーをカスタマイズしたり別の画像やアイコンで表示するには、高度なマーカーでは、google.maps.marker.PinElement
でピン用のオブジェクトを新たに生成したものをマーカーのコンテンツとしてセットする必要があります。
高度なマーカーのカスタマイズ
高度なマーカーは、主にデフォルトのマーカー、画像マーカー(URL、インラインSVG)、カスタム HTML マーカー(任意の HTML、アイコンフォントなど)の3種類の方法のいずれかで表示およびカスタマイズをします。
デフォルトマーカーのカスタマイズ
デフォルトマーカーのデザインのカスタマイズは、主に以下が対象です。
- マーカーの大きさ(scale)
- マーカーの色
- マーカーの輪郭の色
- グリフ(中心のドット)の色、または非表示
デフォルトのマーカーをカスタマイズするには、新たに google.maps.marker.PinElement
クラスでマーカーピン専用のオブジェクト(pin
)を生成し、その要素( pin.element
)をマーカーのコンテンツとして渡します。
// カスタムマーカーピン
const pin = new google.maps.marker.PinElement({
scale: 1.5, // マーカーの大きさ( 等倍: 1)
background: '#e44631', // マーカーの色
borderColor: '#b01500', // マーカーの輪郭の色
glyphColor: '#b01500', // グリフの色
// glyph: '', // グリフを非表示にする場合
});
// マーカーを生成
const marker = new google.maps.marker.AdvancedMarkerElement({
map,
position: { lat: 37.419, lng: -122.02 },
content: pin.element, // カスタマイズしたマーカーの要素をセット
});
参考: マーカーの基本的なカスタマイズ
公式ドキュメントは PinView
というクラスを使用したコードが掲載されていますが、マーカー(AdvancedMarkerView
)同様に現在はそんなクラスは存在しません。PinElement
を使用します。
場所アイコン(SVG)をグリフとして表示する
マップの対象ロケーションが Google によってカテゴライズされている場合、ジャンルごとに適したアイコンが場所情報のデータとして存在しています。
このアイコンをマーカーのグリフとして表示してみましょう。
参考: 割り当てられるアイコン一覧
マップで表示する位置の場所情報を取得するために、Places API (New) を有効化し、テキスト検索(Place Search)の API を利用して得られた 場所アイコンのSVG を、マーカーのグリフとして表示させる場合、公式ドキュメントのサンプルでは、
new google.maps.places.PlacesService(map)
を呼び出して場所情報を取得するようになっていますが、現在はそもそも maps
に places
が存在していないため、PlacesService
を呼び出せず、公式のサンプルコードは参考になりません。
とりあえず場所アイコンを取得してマーカーのグリフとして使用できた方法は以下です。
// マップの表示場所
const place = 'IKEA 神戸';
// 取得する場所情報の項目
// 取得可能な項目一覧: https://developers.google.com/maps/documentation/javascript/reference/place?hl=ja
const fields = [
'svgIconMaskURI', // カテゴライズされた対象の場所のアイコン(これをグリフに使う)
'iconBackgroundColor', // 対象アイコン用の背景カラー
'displayName', // 表示名
'adrFormatAddress', // 対象場所の住所(HTML)
'nationalPhoneNumber', // 対象場所の電話番号(国内向け)
'websiteURI', // 対象場所のウェブサイトアドレス
];
// 取得する場所情報の条件
const request = {
textQuery: place, // マップで表示する場所の名称(店舗名、施設名等)
fields: fields, // 取得する場所情報(上記の fields 配列の内容)
useStrictTypeFiltering: false,
};
// マーカー生成
const marker = new google.maps.marker.AdvancedMarkerElement({
map,
position: map.getCenter(),
});
// 場所情報を取得するためのライブラリ( Place )をインポート
const { Place } = await google.maps.importLibrary( 'places' );
// 条件を元に場所を検索(テキスト検索)
const { places } = await Place.searchByText( request );
// 取得された場所情報のうち、最初のアイテムを利用
if ( places && Array.isArray( places ) && places.length > 0 ) {
// マーカーピンのオプション
const pinOptions = {
scale: 1.5, // マーカーの拡大サイズ(1.5倍)
background: places[ 0 ]?.iconBackgroundColor, // マーカーの背景カラー
glyphColor: '#fff', // グリフのカラー
glyph: new URL( places[ 0 ]?.svgIconMaskURI ), // 取得された SVGアイコンのURLをグリフとしてセット
}
// マーカーピン生成
const pin = new google.maps.marker.PinElement( pinOptions );
// 生成したマーカーピン(pin.element)をマーカーのコンテンツとして代入
marker.content = pin.element;
// マーカーをマウスオーバーした際に表示されるツールチップテキスト(場所情報の表示名)
marker.title = places[ 0 ]?.displayName;
}
参考: Place Search
参考: Place (取得可能な場所情報一覧 ※ Places API (New) の場合)
参考: 場所のアイコンと背景色のリクエスト
参考: 場所アイコンをグリフとして使用する ※サンプルコードは参考にならない
こちらの場所情報一覧は Places API (New) ではなく Places API の場合に指定可能な項目なので参考にしないこと。
例えば、営業時間を取得するためにopeningHours
を指定すると Places API (New)では非推奨となっており InvalidValue エラーが発生するため、regularOpeningHours
を指定する。
マーカーを任意の画像で表示する
マーカーを画像で表示する場合は、JavaScript で動的に生成した img 要素をマーカーのコンテンツとしてセットするのみです。
// マーカー用画像を動的に生成
const markerImg = document.createElement( 'img' );
markerImg.src = "https://YOUR_SITE.com/marker.png"; // マーカー用画像
markerImg.width = '90'; // マーカー用画像の表示幅
// マーカーを生成
const marker = new google.maps.marker.AdvancedMarkerElement({
map,
position: { lat: 37.434, lng: -122.082 },
content: markerImg, // 生成したマーカー用画像をセット
});
マーカー画像の表示サイズ(幅もしくは高さ)を指定しないと色々と表示上えらいことになるので必ず画像に合わせた適切なサイズを指定しておくとよいです。
その他、インラインSVG をマーカーとして表示することもできます。
その他のカスタムマーカーの生成方法は公式ドキュメントを参照してください。
参考: インライン SVG を使用する
カスタム HTML でマーカーを表示する
マーカーを独自の HTML で構築し、マーカー用の CSS を用意することで完全にオリジナルのマーカーを表示できます。
HTML なので色、形、レイアウト、表示する内容(リンク、画像など)に制限がないため、リッチコンテンツのマーカーを表示したい場合はこの方法でマーカーを自作します。
const initMap = async () => {
// マップ生成
const map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 37.42, lng: -122.1 },
zoom: 14,
mapId: "4504f8b37365c3d0",
});
// マーカー用カスタムHTML
const markerElement = document.createElement("div");
// class をセット
markerElement.className = "price-tag";
markerElement.innerHTML = "<span class=\"price\">¥9,800</span> (1泊)<br /><span class="discount">15% Off!</span>";
// マーカーを生成
const marker = new AdvancedMarkerElement({
map, // 対象のマップ
position: { lat: 37.42, lng: -122.1 }, // 中心位置
content: markerElement, // カスタムHTMLマーカーをセット
});
}
initMap();
マーカーをアニメーションで表示する
先述の通り、高度なマーカーでは従来のマーカーのアニメーションメソッド( Marker.setAnimation()
)は存在しません。
従来のマーカーでアニメーション(BOUNCE
または DROP
)を利用していた場合は、同じようにアニメーションを反映させるには、同様の動作をする CSS アニメーション(キーフレーム、セレクタ)を自作し、AdvancedMarkerElement
で生成したマーカーの要素の class としてアニメーション用のセレクタを追加する必要があります。
// マーカー生成
const marker = new google.maps.marker.AdvancedMarkerElement({
map,
position: { lat: 37.419, lng: -122.02 },
});
// アニメーション用セレクタを追加
marker.content.classList.add( 'bounce' );
.bounce {
animation: bounce 1s ease-in-out infinite;
}
@keyframes bounce {
0%, 100% {
transform: translateY( 0 );
}
50% {
transform: translateY( -20px );
}
}
マーカーアニメーションの仕様も根こそぎ仕様が変わりましたが、従来のアニメーションは Bounce と Drop の2種類のプリセットのみに対し、高度なマーカーでは自作する必要こそありますが、その分 CSS アニメーションで自由に動きをカスタマイズできるようになっています。
高度なマーカーの更新
従来のマーカーで提供されていた各種メソッドも高度なマーカーでは存在しないため、マーカーの内容をまとめて一括で更新する際に利用していた setOptions
メソッドで更新しようとしてもエラーが発生して更新されません。
高度なマーカーでは、マーカーの各プロパティの値を直接更新します。
const map = new google.maps.Map( canvas, options );
const marker = new google.maps.marker.AdvancedMarkerElement( {
map,
position: map.getCenter()
title: initTitle,
} );
// マーカーを更新
useEffect( () => {
marker.position = newLocation;
marker.title = newTitle;
marker.content = newMarkerContent;
}, [ newLocation, newTitle, newMarkerContent ] );
その他、従来のマーカーのメソッドを利用した処理はすべて修正する必要があります。