DigiPress

Highly Flexible WordPress Theme

メニュー

Google Maps API のマーカー仕様が刷新されたので対処してみた

今さらなのですが、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.Markergoogle.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)

を呼び出して場所情報を取得するようになっていますが、現在はそもそも mapsplaces が存在していないため、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\">&yen9,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();

参考: カスタム HTML を使ったマーカーを作成する

マーカーをアニメーションで表示する

先述の通り、高度なマーカーでは従来のマーカーのアニメーションメソッド( Marker.setAnimation() )は存在しません。
従来のマーカーでアニメーション(BOUNCE または DROP)を利用していた場合は、同じようにアニメーションを反映させるには、同様の動作をする CSS アニメーション(キーフレーム、セレクタ)を自作し、AdvancedMarkerElement で生成したマーカーの要素の class としてアニメーション用のセレクタを追加する必要があります。

JavaScript
// マーカー生成
const marker = new google.maps.marker.AdvancedMarkerElement({
map,
position: { lat: 37.419, lng: -122.02 },
});

// アニメーション用セレクタを追加
marker.content.classList.add( 'bounce' );
CSS
.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 ] );

その他、従来のマーカーのメソッドを利用した処理はすべて修正する必要があります。


Share / Subscribe
Facebook Likes
Tweets
Hatena Bookmarks
Pinterest
Pocket
Feedly
Send to LINE