📍

AdvancedMarkerElement に移行しようとして見送った話 | Google Map

2024/09/03に公開

SakeDoko では Google Maps API を使用して地図を表示しています。
前々からコンソールに以下の警告が表示されるようになりました。

google.maps.Marker のサポートが切れたので google.maps.marker.AdvancedMarkerElement に移行してください。
ということで、ドキュメントを見ながら移行作業を進めてみました。

その中でいろいろと詰まったことがあり、最終的に現段階での移行を断念するという判断になったので経緯をまとめておいて、忘れた頃にスムーズな移行ができるように備忘録として残しておこうと記事を書いています。
Google Map の API を使っている人の参考になれば嬉しいです。

(以下、google.maps.MarkerMarkergoogle.maps.marker.AdvancedMarkerElementAdvancedMarkerElement と略記)

前提

  • Next.js 14.2.4
  • google-map-react 2.2.1
  • @googlemaps/markerclusterer 2.5.3

Marker と AdvancedMarkerElement の差分

https://developers.google.com/maps/documentation/javascript/advanced-markers/migration?hl=ja

マップIDを使用する必要がある

上記のドキュメントに書いてあるとおり、マップIDを取得して紐付ける必要があります。
マップIDの作成方法は以下リンクを参照します。
https://developers.google.com/maps/documentation/get-map-id?hl=ja

google-map-react では以下のようになります。

<GoogleMapReact
    bootstrapURLKeys={{
        key: process.env.NEXT_PUBLIC_GOOGLE_MAP_KEY,
        // AdvancedMarkerElement を使用するには 'marker' は必須
        libraries: ['marker', 'geometry', 'drawing', 'places'],
    }}
    options={{
        // mapId を使用すると styles は使えなくなる(後述)
        // styles: mapStyles,
        mapId: process.env.NEXT_PUBLIC_GOOGLE_MAP_ID,
    }}
>
    {/* 任意のコンポーネント */}
</GoogleMapReact>

※ 実際に運用しているコードでは上記以外のパラメータもあります。

任意のパラメータを直接入れられない

Marker の場合は任意のパラメータを含めることができました。
以下のコードの postCreatedAt がそれに当たります。
これによって SakeDoko では投稿データに応じてマーカーのアイコン画像を切り替えています。

export const displayMarker = (
    posts.forEach((post) => {
        const marker = new maps.Marker({
            map,
            position: { lat: post.lat, lng: post.lng },
            icon: {
                url: iconGlobalIconPath(post.created_at),
                scaledSize: new google.maps.Size(iconSize(post.created_at), iconSize(post.created_at)),
            },
            // 任意のパラメータ
            postCreatedAt: post.created_at,
        })

       // ...
    })
)

ですが、 AdvancedMarkerElement では直接的に任意のパラメータを含めることはできなくなります。
同様のことを実現するのであれば、後述するパラメータ content に含める方法で代替します。

マーカーはHTMLでカスタマイズ

では、前述のコードはどのように書き換える必要があるのか。
まずは書き換えたコードを以下に示します。

posts.forEach((post) => {
    const marker = new maps.marker.AdvancedMarkerElement({
        map,
        position: { lat: post.lat, lng: post.lng },
        // icon パラメータも content に集約する必要がある
        content: createMarkerElement(post.created_at),
})

content にはHTML要素を含めることができます。
なので、先ほど Marker に含めていたパラメータは以下のように dataset に含めるようにします。

export const createMarkerElement = (createdAt: Date): HTMLElement => {
    const iconElement = document.createElement('img')
    iconElement.src = iconGlobalIconPath(createdAt)
    iconElement.style.width = `${iconSize(createdAt)}px`
    iconElement.style.height = `${iconSize(createdAt)}px`
    iconElement.dataset.postCreatedAt = `${createdAt}`

    return iconElement
}

こうすることによって、 marker.content.dataset.postCreatedAt で参照できるようになります。

マーカークラスターも対応

SakeDoko では通常のマーカーとは別にクラスターマーカーを活用し、近接するマーカーをクラスタに統合し、地図上のマーカー表示を簡素化しています。
以下の地図で中央にマーカーの統合数が表示されているのがクラスターマーカーです。

移行前のコード

移行前のクラスターマーカー描画部分のコードです。

interface ExtendedMarker extends google.maps.Marker {
    postCreatedAt: Date | null
}

const renderer: Renderer = {
    render: ({ markers, position }) => {
        const extendedMarkers = markers as ExtendedMarker[]
        if (!extendedMarkers || extendedMarkers.length === 0) return null

        // クラスタ内のマーカーの中に1週間以内の投稿があるかどうかをチェック
        const isRecentCluster = extendedMarkers.some((marker) => {
            // 省略
        })
        
        // アイコンのURLを設定
        const iconUrl = isRecentCluster
            ? https:sakedoko.hogehoge.com/icon.png   // オリジナルのアイコン
            : `data:image/svg+xml;base64,${svg}`     // デフォルトのアイコン
        
        return new maps.Marker({
            position,
            icon: { url: iconUrl, scaledSize: new google.maps.Size(35, 35) },
            // アイコンの中央に統合マーカー数を表示
            label: {
              text: String(extendedMarkers?.length),
              fontSize: '10px',
              color: 'white',
            },
        })
    },
}

new MarkerClusterer({ map, markers, renderer })

移行後のコード

以下のように、 label がなくなり content に集約されます。

type ExtendedMarker = {
    element: google.maps.marker.AdvancedMarkerElement
    content: {
        dataset: {
          postCreatedAt: Date
        }
    }
}

const renderer: Renderer = {
    render: ({ markers, position }): google.maps.marker.AdvancedMarkerElement => {
        const extendedMarkers = markers as unknown as ExtendedMarker[]
        
        if (!extendedMarkers || extendedMarkers.length === 0) {
            return new maps.marker.AdvancedMarkerElement({
              position,
              content: document.createElement('div'), // 空のdiv要素をcontentに設定
            })
        }
        
        // クラスタ内のマーカーの中に1週間以内の投稿があるかどうかをチェック
        const isRecentCluster = extendedMarkers.some((marker) => {
            // 省略
        })
        
        return new maps.marker.AdvancedMarkerElement({
            position,
            // label も content に集約する必要がある
            // createClusteerIconElement 関数の中で label の要素を生成している(内容は省略)
            content: createClusterIconElement(isRecentCluster, extendedMarkers),
        })
    },
}

new MarkerClusterer({ map, markers, renderer })

問題 地図のスタイルを詳細にカスタマイズできない

マーカーもクラスターマーカーの移行も完了して一安心...と思いきや、なんだか地図のスタイルがデフォルトに戻って見にくくなっているし、コンソールに警告も出ている。

マップIDを使う場合は、クラウドコンソールから設定しないとスタイルは適用されないということ。
ではクラウドコンソールからスタイルの設定方法について調べてみます。

https://developers.google.com/maps/documentation/javascript/cloud-customization/map-styles?hl=ja#import-json-styling

  1. Google Cloud コンソールで、[地図のスタイル] ページに移動します。
  2. [スタイルを作成] をクリックします。
  3. [独自のスタイルの作成] 欄で、[JSON をインポート] ラジオボタンを選択します。
  4. 有効な JSON スタイルコードをフィールドに貼り付けます。
    ・JSON が無効な場合は、JSON フィールドのすぐ下に大文字で通知が表示されます。
    ・JSON が有効な場合、貼り付けたスタイルのプレビューが表示され、青色の [保存] ボタンを使用できます。
  5. [保存] を選択します。

スタイルが自動的に公開され、スタイルのメインページが表示されます。

JSONでもインポートできるようなので手順2まで進んでみたものの [独自のスタイルの作成]欄がない!
これは私だけなのか..と思いきやどうやら違うようでした。

https://issuetracker.google.com/issues/321086834?pli=1

  • AdvancedMarkerElement に移行するにはマップIDを使わなければいけない。
  • クラウドコンソールから地図のスタイルをカスタマイズする必要があるが、詳細なカスタマイズができない。

SakeDokoでは独自のマーカーを使用しており、視認性を高めるために地図の明度を設定しています。

しかしながら、クラウドコンソールからは明度の設定は見当たりませんでした。
ドキュメントに書いてありながら JSON でインポートもできなければ、JSON でできたカスタマイズもクラウドコンソールではできない状態です。
こうなるともう Google の対応を待つしかありません...。

終わりに

サポートが終了した Marker ですが、未だ Marker で書かれているドキュメントがほとんどです。
ちょっとツッコミたくなりますね。
以下の記事も参考にさせていただきまして、こちらの記事でも「情報がとっ散らかっていて翻弄されます」と述べているように私も翻弄されました。

https://digipress.info/tech/migrate-to-google-maps-api-advanced-marker-element/

最後 Google 批判のようになってしまいましたが、Google Maps API はとても便利で SakeDoko のメイン機能としてなくてはならない存在です。

ここまで読んでいただきましてありがとうございました。
日本酒がお好きな方はぜひ 日本酒体験が地図になる SakeDoko を使ってみてください!

https://sakedoko.jp/?utm_source=zenn&utm_medium=a8e1fb23e7b371&utm_content=fin

Discussion