今日はGoogle Map(グーグルマップ)のAPIを使ってカスタムマップを作成してみましょう!ワードプレスを使ったことがある人は簡単にプラグインを使って、カスタムマップを作成することができますね。今日は、同じことをマニュアルで行います。
これで不動産の物件を地図に表示したり、お店の位置情報を紹介することなどに使えたりすると思います。
完成したコードはGitHubから見てください。
注意
クレジットカードの登録なしでもAPIコールはかけられますが、Googleからのエラーのポップアップが表示されます。
もし、一般ユーザー向けにAPIを使用したい場合は、アカウントの登録を完了してください。クレジットカードの登録が必要になります。
2023年の2月時点での参考料金表になります。
今日の環境
- npmが使えること
- JavaScriptの基礎が分かること
サンプルプロジェクトの確認
実際にコードを書く前にどのようなことができるのかデモで確認したい人はこの手順を追ってください。
GoogleマップのGitHubリポをクローンしてGoogleのデモGoogleマップを見てみます。GitとGitHubの使い方はこちらでも説明しています。
git clone https://github.com/googlemaps/js-samples.git
cd js-samples/
npm i
npm start
これでGoogleマップのデモが開くのでカスタムマーカーを使ったマップもこのように確認することができますね。
Playgroundというボタンをクリックするとコードを見ながら実際にどのように表示されるか試すことができます。
では、早速Google CloudからMAP APIのサービスを使っていきましょう!
JavaScriptのプロジェクト作成
では今日はViteを使ってプロジェクトを作成します。Viteはビルドツールで、ReactやVueのアプリケーションを使う際に使う事がほとんどですが、Vanilla JS(普通のJavaScript)でも使う事ができます。
ではコマンドプロンプト(もしくはターミナル)を開いて下記の様に入力していきます。
npm create vite@latest
#結果
✔ Project name: … custom-google-map
✔ Select a framework: › Vanilla
✔ Select a variant: › JavaScript
Scaffolding project in /home/dan/Documents/public/customMap/custom-google-map...
Done. Now run:
cd custom-google-map
npm install
npm run dev
では、npm i (installのi)のコマンドでプロジェクトができたらテキストエディタを開きましょう。
また、デフォルトのページをテストサーバーで見たい際はnpm run devのコマンドで確認することができます。
viteで作成されたmain.jsがデフォルトのJavaScriptのエントリーポイントになります。
Google Map APIを作る
では、コードに入る前にgoogle Cloudを使ってGoogle MapのAPIキーを作成します。まずは、googleクラウドのページにアクセスします。
右上の無料で利用開始をクリックします。
では、フォームを入力していきます。
では、次のページも完了させ登録を完了させてください。
次にコンソールのページが見れるのでそこから、プロジェクトの選択をクリックします。
次に、新しいプロジェクトをクリックします。
次に、プロジェクト名を適当に入れて、作成ボタンを押します。
プロジェクトが作成できたら、メイン画面の右上にプロジェクトを選択するリンクが表示されるのでそれをクリックします。
次にGoogle Cloudの左上のタブから①APIとサービス、②Google Map Platformの二つを探してピンしておきましょう。ピンはホバーしたときに表示される画びょうのアイコンでショートカットのように上部に持ってこられるので今後探す手間が省けます。
カスタムマップの作成
では、Google Cloudからカスタムマップを作成します。
Google Maps Platformからマップ管理を選択します。
次にCREATE MAP IDを選択します。
次に、マップの情報を入力します。
名前は、自分のプロジェクトを適当に入力します。説明も同じように適当に入れます。Map Typeはデスクトップで表示させたい場合は、JavaScriptを選択します。
下にあるRasterとVectorの選択はたデフォルトのRasterを選択します。Vectorでも良いですが、ブラウザによって対応していないものもあるので注意。
これでMap IDができました。あとからMap IDを使うのでどこかにコピーしておきましょう。
マップのスタイルを作る
次にマップのスタイリングをします。
マップ管理のぺージから地図のスタイルを選択し、上のCREATE STYLEをクリックします。
好きなスタイル(Atlas、ライト、グレーなど)を選択して、保存します。場合によっては旅行、不動産、ロジスティクス(物流)などを選択してもOKです。
ポップアップで、マップのスタイルの情報を入力できるので入れておきます。
次に、Map StylesのCUSTOMIZE STYLEをクリックします。
ここで非表示にしたい建物の種類などを選択し、マップのスタイル(色)を変えてみましょう。
例えば、道路やスポットを選択して、テキストをOFFにすると、マップから名称が表示されなくなりましたね。
変更が完了した後に保存をクリックし」、Publishを押してプロジェクトクトを公開します。
ではこのマップのスタイルを公開するグーグルマップに割り当ててあげます。
マップ管理からMap IDをクリックします。
マップスタイルのドロップダウンから、先ほど作成したマップを選択します。
では、保存のボタンを押して適用させます。
次にマップを読み込む際にMAP IDが必要になるのでどこかにコピーしておきましょう。
MAP ID: 876806baa0024d81
Google MapのAPIキーを取得
次にGoogle Cloud コンソールのメニューからマーケットプレイスを選択します。
次に検索バーに”Maps JavaScript API”と入力します。
そこでMaps JavaScript APIのツールが表示されるのでEnableを押して使えるようにします。
次に、再度、Google Cloudコンソールのページに戻り、メニューからAPIsを選択し、認証情報を選択します。
次に、上部のCREATE CREDENTIALSのボタンをクリックして、API Keyを選択します。
そうするとAPIキーが生成されるのでどこかにコピーしておきます。
APIキーはとても重要な個人情報です。これを使って、他人が勝手にAPIを使うこともできます。(有料のサービスを使用する場合は勝手に課金されてしまうことも。)
必ず、他の人にシェアしないように気をつけてください。
また、上記画像にある、APIキーを編集するというリンクから、アクセスを制限できるので設定しておきましょう。
そこでは、IPアドレスやドメインなどからAPIキーの使用を制限できるようになっています。今回は省きますが是非、設定しておきましょう。
クレジットカードの登録
APIを利用するためにはアカウントの作成+クレジットカードの登録が必要になります。さらにアカウントの登録で$300相当のクレジットがもらえるのでテスト程度の操作では、お金がかかることはないですが、注意をして使用しましょう。(※私は一切の責任を負いません。)
登録が完了できたらAPIを有効にして使えるようにしましょう。
コーディング
ではGoogle MapのAPIができたところでやっとコーディングに入ります。
こちらにGooleマップJavaScriptAPIのドキュメンテーションがあるので参考にしてください。同じ内容のものを今から説明します。
カスタムしたマップのスタイルをインポートします。
index.htmlをこのように書きます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="module" src="/main.js"></script>
<link rel="stylesheet" type="text/css" href="./style.css" />
<title>Map</title>
</head>
<body>
<h1>Welcome to my custom map!</h1>
<div id="map"></div>
<script
src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=initMap&v=weekly"
defer
></script>
</body>
</html>
index.htmlの記載で注意
- style.cssのようにCSSファイルをインポートしていること。(後でマップのスタイルを書きます)
- main.jsをインポートしていること
- idがmapのdivタグを作成します。(ここにマップをロードします)
- scriptタグでgoogleマップのAPIを呼びます。
最後のGoogleマップの部分を説明します。
<script
src="https://maps.googleapis.com/maps/api/js?key=API_KEY&callback=initMap&v=weekly"
defer
></script>
API_KEYの部分を自分のAPIキーに入れ替えてください。あと、見ていただきたいのがcallbackの後に来るinitMapで、APIのデータが返ってきたあとにinitMapの関数を実行させていることです。このinitMap関数は次に書きます。
main.jsの記載
let map;
function initMap() {
new google.maps.Map(document.getElementById("map"), {
mapId: "876806baa0024d81",
center: { lat: 48.85, lng: 2.35 },
zoom: 12,
});
}
window.initMap = initMap;
ではJavaScriptファイルにはこのように記載します。
まずは、APIのコールバックで実行されるinitMapの関数を書きます。
- 前回にコピーしておいたマップIDを記載します。
- centerには、マップの表示の際に真ん中に表示したい緯度、経度を記載します。
- zoomではマップをどれだけ拡張もしくは縮小させて表示させたいか記載します。
緯度と経度は通常のGoogleマップで対象の場所を右クリックすると表示されるのでコピペして使います。
zoomは20が建物を表示できるほどにズームされたマップになります。適当に数字を入れて好みに合わせてください。
次にstyles.cssを記載します。
#map {
height: 100%;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
最低限にmapの高さを持たせてください。もしうまくいかない人は、縦と横幅を固定して試してみてもOKです。
では、テストようのサーバーを起動してみてみましょう。
npm run dev
これでマップが表示されましたね。
さらにマップIDからマップのスタイルも読み込まれていることが確認できます。今回の場合はすべての建物名を非表示にしました。
マーカーを表示させる
では、カスタムマーカーをSVGファイルなどの画像ファイルで用意します。もしくはパスデータでもOKです。
google.maps.Marker コンストラクタを使用して、マーカーオブジェクトを作成します。
以下、Googleからの説明です:
マーカーは、地図上の場所を特定するものです。デフォルトでは、マーカーは標準画像を使用しますが、カスタム画像を表示することも可能です。この場合、通常は「アイコン」と呼ばれます。マーカーとアイコンは、Marker
タイプのオブジェクトです。カスタム アイコンは、マーカーのコンストラクタ内に設定するか、マーカー上で setIcon()
を呼び出して設定できます。詳しくは、マーカー画像をカスタマイズするをご覧ください。
大まかに言うと、マーカーはオーバーレイの一種です。オーバーレイのその他の種類については、地図上での図形描画をご覧ください。
マーカーは、インタラクティブに動作するように設計されています。たとえば、デフォルトでマーカーは 'click'
イベントを受信します。したがって、イベント リスナーを追加すると、カスタム情報を表示する情報ウィンドウを表示できます。マーカーの draggable
プロパティを true
に設定して、ユーザーに地図上のマーカーの移動を許可することもできます。ドラッグ可能なマーカーについて詳しくは、下記をご覧ください。
以上です。
では、先ほど記述したinitMap関数にマーカーも合わせて読み込ませるようにします。基本はこのようになります。
このようにgoogle.maps.Markerオブジェクトを作成します。
function initMap() {
const myLatLng = { lat: -25.363, lng: 131.044 };
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: myLatLng,
});
new google.maps.Marker({
position: myLatLng,
map,
title: "Hello World!",
});
}
window.initMap = initMap;
では実際にこのように記載してみましょう。
- map変数を作成して、googlemapの情報を格納します。
- マーカーオブジェクトにマップ情報を渡してあげます。
- postionに表示したい緯度と経度を記載します。
- titileにホバーしたときに表示させたいタイトルを記載します。(なくてもOK)
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
mapId: "876806baa0024d81",
center: { lat: 40.73090330192132, lng: -73.99024079492982 },
zoom: 17,
});
new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
map,
title: "一風堂",
});
}
window.initMap = initMap;
これでマップにデフォルトのアイコンが表示されましたね。
カスタムマーカーを表示させる
では、カスタムマーカを表示させてみましょう。
このようにpngの画像ファイルの場合はこのように設定できます。
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: { lat: -33, lng: 151 },
});
const image =
"https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png";
const beachMarker = new google.maps.Marker({
position: { lat: -33.89, lng: 151.274 },
map,
icon: image,
});
}
window.initMap = initMap;
SVGマーカの場合はこのように設定できます。
const svgMarker = {
path: "M10.453 14.016l6.563-6.609-1.406-1.406-5.156 5.203-2.063-2.109-1.406 1.406zM12 2.016q2.906 0 4.945 2.039t2.039 4.945q0 1.453-0.727 3.328t-1.758 3.516-2.039 3.070-1.711 2.273l-0.75 0.797q-0.281-0.328-0.75-0.867t-1.688-2.156-2.133-3.141-1.664-3.445-0.75-3.375q0-2.906 2.039-4.945t4.945-2.039z",
fillColor: "blue",
fillOpacity: 0.6,
strokeWeight: 0,
rotation: 0,
scale: 2,
anchor: new google.maps.Point(15, 30),
};
new google.maps.Marker({
position: map.getCenter(),
icon: svgMarker,
map: map,
});
では実際の例ではこのように記載できます。
これはパスデータを使った例です。
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
mapId: "876806baa0024d81",
center: { lat: 40.73090330192132, lng: -73.99024079492982 },
zoom: 17,
});
const svgMarker = {
path: "M10.453 14.016l6.563-6.609-1.406-1.406-5.156 5.203-2.063-2.109-1.406 1.406zM12 2.016q2.906 0 4.945 2.039t2.039 4.945q0 1.453-0.727 3.328t-1.758 3.516-2.039 3.070-1.711 2.273l-0.75 0.797q-0.281-0.328-0.75-0.867t-1.688-2.156-2.133-3.141-1.664-3.445-0.75-3.375q0-2.906 2.039-4.945t4.945-2.039z",
fillColor: "blue",
fillOpacity: 0.6,
strokeWeight: 0,
rotation: 0,
scale: 2,
anchor: new google.maps.Point(15, 30),
};
new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
icon: svgMarker,
map,
title: "一風堂",
});
}
window.initMap = initMap;
ズームレベルが変更されたときにマーカーが正しくレンダリングされるようにするには、anchor
プロパティが必要です。
これで、カスタムアイコンがロードされましたね。
もしくは、SVGで表示したい場合はこのようにも書けます。
new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
icon: {
url: "./javascript.svg",
scaledSize: new google.maps.Size(38, 31),
},
map,
title: "一風堂",
});
これで、SVGファイルがロードされました。今回は、ViteについてきたJavaScriptのアイコンをロードしています。
ロード時にアイコンにドロップアニメーションを付ける
では、下記のプロパティを追加してマップがロードされたときにマーカーが落ちるアニメーションを追加してみましょう。animationの部分を追加してください。
new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
icon: {
url: "./javascript.svg",
scaledSize: new google.maps.Size(38, 31),
},
map,
title: "一風堂",
animation: google.maps.Animation.DROP,
});
これでブラウザをリロードするとこのようにアニメーションが追加されました。
カスタムポップアップを作成する
ではマーカーをクリックしたときに表示されるポップアップを作成してみましょう。ドキュメンテーションはこちらです。
以下ドキュメンテーションからの引用です:
InfoWindow
コンストラクタは、情報ウィンドウを表示する際の初期パラメータを指定する InfoWindowOptions
オブジェクト リテラルを取得します。
InfoWindowOptions
オブジェクト リテラルには次のフィールドがあります。
-
content
には、情報ウィンドウに表示するテキスト文字列または DOM ノードが含まれます。 -
pixelOffset
には、情報ウィンドウの先端から情報ウィンドウを固定する場所までのオフセットが含まれます。実際には、このフィールドを指定する必要はありません。デフォルト値のままでも問題ありません。 -
position
には、この情報ウィンドウを固定する位置を示すLatLng
が含まれます。注:InfoWindow
は、Marker
オブジェクトに付着させることも(この場合、位置はマーカーの場所に準じます)、地図自体で指定されたLatLng
に付着させることも可能です。LatLng
を取得する 1 つの方法は、ジオコーディング サービスを使用する方法です。マーカー上で情報ウィンドウを開くと、position
は自動的に更新されます。 -
maxWidth
では、情報ウィンドウの最大幅をピクセル単位で指定します。デフォルトでは、情報ウィンドウはそのコンテンツが収まるように拡大され、情報ウィンドウが地図と同じ幅になると自動的に改行が行われます。maxWidth
を追加すると、情報ウィンドウは指定された幅以上には拡大されず、自動的に改行が行われます。最大幅に達し、画面の垂直方向に余裕がある場合、情報ウィンドウは垂直方向に延伸します。
InfoWindow
のコンテンツには、テキスト文字列、HTML スニペット、または DOM 要素を指定できます。コンテンツを設定するには、InfoWindowOptions
内でコンテンツを指定するか、InfoWindow
で明示的に setContent()
を呼び出します。
コンテンツのサイズを明示的に指定するには、コンテンツを <div>
要素内に配置し、CSS を使用して <div>
のスタイルを設定します。CSS を使用してスクロールを有効にすることも可能です。スクロールを有効にせず、コンテンツが情報ウィンドウの使用可能スペースを超過した場合、コンテンツは情報ウィンドウからはみ出します。
以上です。
こちらがドキュメンテーションにあるコードの例になります。
function initMap() {
const uluru = { lat: -25.363, lng: 131.044 };
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: uluru,
});
const contentString =
'<div id="content">' +
'<div id="siteNotice">' +
"</div>" +
'<h1 id="firstHeading" class="firstHeading">Uluru</h1>' +
'<div id="bodyContent">' +
"<p><b>Uluru</b>, also referred to as <b>Ayers Rock</b>, is a large " +
"sandstone rock formation in the southern part of the " +
"Northern Territory, central Australia. It lies 335 km (208 mi) " +
"south west of the nearest large town, Alice Springs; 450 km " +
"(280 mi) by road. Kata Tjuta and Uluru are the two major " +
"features of the Uluru - Kata Tjuta National Park. Uluru is " +
"sacred to the Pitjantjatjara and Yankunytjatjara, the " +
"Aboriginal people of the area. It has many springs, waterholes, " +
"rock caves and ancient paintings. Uluru is listed as a World " +
"Heritage Site.</p>" +
'<p>Attribution: Uluru, <a href="https://en.wikipedia.org/w/index.php?title=Uluru&oldid=297882194">' +
"https://en.wikipedia.org/w/index.php?title=Uluru</a> " +
"(last visited June 22, 2009).</p>" +
"</div>" +
"</div>";
const infowindow = new google.maps.InfoWindow({
content: contentString,
ariaLabel: "Uluru",
});
const marker = new google.maps.Marker({
position: uluru,
map,
title: "Uluru (Ayers Rock)",
});
marker.addListener("click", () => {
infowindow.open({
anchor: marker,
map,
});
});
}
window.initMap = initMap;
では、まずinfowindowオブジェクトを作成しましょう。こちらをコピーして自分のコードに入れてください。
const infowindow = new google.maps.InfoWindow({
content: contentString,
ariaLabel: "Uluru",
});
次に、クリックイベントを追加します。
marker.addListener("click", () => {
infowindow.open({
anchor: marker,
map,
});
});
さらに、アンカーで使用している変数markerがないので、const markerを作成して、既存のマーカーに割り当ててあげましょう。
const marker = new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
icon: {
url: "./javascript.svg",
scaledSize: new google.maps.Size(38, 31),
},
map,
title: "一風堂",
animation: google.maps.Animation.DROP,
});
実際のコードはこのようになります。
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
mapId: "876806baa0024d81",
center: { lat: 40.73090330192132, lng: -73.99024079492982 },
zoom: 17,
});
const infowindow = new google.maps.InfoWindow({
content: "If you want to eat Tonkotsu Ramen in NY...",
});
const marker = new google.maps.Marker({
position: { lat: 40.73090330192132, lng: -73.99024079492982 },
icon: {
url: "./javascript.svg",
scaledSize: new google.maps.Size(38, 31),
},
map,
title: "一風堂",
animation: google.maps.Animation.DROP,
});
marker.addListener("click", () => {
infowindow.open({
anchor: marker,
map,
});
});
}
window.initMap = initMap;
では実際にブラウザで確認するとポップアップが表示されるようになりました。
複数のマーカーを表示させる
では複数のマーカを表示させてみましょう。詳細はこちらのドキュメンテーションでも説明しています。
ドキュメンテーションの例のように複数のマーカーが表示できます。
サンプルコードを見てみましょう。
// The following example creates complex markers to indicate beaches near
// Sydney, NSW, Australia. Note that the anchor is set to (0,32) to correspond
// to the base of the flagpole.
function initMap() {
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 10,
center: { lat: -33.9, lng: 151.2 },
});
setMarkers(map);
}
// Data for the markers consisting of a name, a LatLng and a zIndex for the
// order in which these markers should display on top of each other.
const beaches = [
["Bondi Beach", -33.890542, 151.274856, 4],
["Coogee Beach", -33.923036, 151.259052, 5],
["Cronulla Beach", -34.028249, 151.157507, 3],
["Manly Beach", -33.80010128657071, 151.28747820854187, 2],
["Maroubra Beach", -33.950198, 151.259302, 1],
];
function setMarkers(map) {
// Adds markers to the map.
// Marker sizes are expressed as a Size of X,Y where the origin of the image
// (0,0) is located in the top left of the image.
// Origins, anchor positions and coordinates of the marker increase in the X
// direction to the right and in the Y direction down.
const image = {
url: "https://developers.google.com/maps/documentation/javascript/examples/full/images/beachflag.png",
// This marker is 20 pixels wide by 32 pixels high.
size: new google.maps.Size(20, 32),
// The origin for this image is (0, 0).
origin: new google.maps.Point(0, 0),
// The anchor for this image is the base of the flagpole at (0, 32).
anchor: new google.maps.Point(0, 32),
};
// Shapes define the clickable region of the icon. The type defines an HTML
// <area> element 'poly' which traces out a polygon as a series of X,Y points.
// The final coordinate closes the poly by connecting to the first coordinate.
const shape = {
coords: [1, 1, 1, 20, 18, 20, 18, 1],
type: "poly",
};
for (let i = 0; i < beaches.length; i++) {
const beach = beaches[i];
new google.maps.Marker({
position: { lat: beach[1], lng: beach[2] },
map,
icon: image,
shape: shape,
title: beach[0],
zIndex: beach[3],
});
}
}
window.initMap = initMap;
この例にあるbeaches変数のように配列に各マーカーの位置情報などを格納して読み込ませるようにしましょう。
ではmapメソッドを使ってmarkersのデータをループしてgoogleマップで使えるオブジェクトにしていきます。
let map;
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
mapId: "876806baa0024d81",
center: { lat: 40.73090330192132, lng: -73.99024079492982 },
zoom: 17,
});
const markers = [
{
title: "一風堂",
lat: 40.73090330192132,
lng: -73.99024079492982,
url: "./javascript.svg",
width: 38,
height: 38,
},
{
title: "ICHIRAN Ramen ",
lat: 40.76010005715904,
lng: -73.98328257794572,
url: "./javascript.svg",
width: 38,
height: 38,
},
];
markers.map((currMarker) => {
const marker = new google.maps.Marker({
position: { lat: currMarker.lat, lng: currMarker.lng },
icon: {
url: currMarker.url,
scaledSize: new google.maps.Size(currMarker.width, currMarker.height),
},
map,
title: currMarker.title,
animation: google.maps.Animation.DROP,
});
const infowindow = new google.maps.InfoWindow({
content: currMarker.title,
});
marker.addListener("click", () => {
infowindow.open({
anchor: marker,
map,
});
});
});
}
window.initMap = initMap;
今回は2つのデータを表示できるようにしましたが、このmarkersオブジェクトを追加することでいくつでも表示できるようになりました。
Googleドキュメンテーションよりも、objectをmapメソッドでループさせる方が現場で使いやすいと思います。
他にも線を引いたり、オバーレイで画像を表示させたり色々できるようなので是非、試してみましょう。
お疲れ様でした。
良かったら私のウェブ開発に関するブログを見てみてください。