React基盤のサイトに簡単に店舗地図を載せる方法について紹介します

:::note info
これは [MIERUNE Advent Calendar 2022](https://qiita.com/advent-calendar/2022/mierune) 6日目の記事です。
昨日は@northprintさんによる[地図で音を鳴らす](https://qiita.com/northprint/items/9a8950dc9f363ca470a4)でした。
:::

店舗のHPを作るときに店舗一覧を示した地図を載せたいんですが、載せられないことがありますでしょうか？

確かにウェブ地図を導入するには：
- 地図APIを利用する場合はコストが嵩む
- 地図ライブラリをカスタマイズできる人が少ない
- 地図の導入が難しい
など、地図を専門とするエンジニアにとっては取り扱うのが難しいです。

特にコスト面も考えると、地図を一枚の画像にしたり、地図をなくすなど、ウェブ地図を使わないことを選んでしまう方も多いかと思います。

しかし、地図を画像にすることは地図会社の規約違反になる可能性があるだけでなく、顧客体験もあまり良くないです。

ウェブサイトに地図を導入しない選択肢もありますが、住所だけではお店がどこにあるのかを大多数の方に伝えることが難しい。

そこで、今日はお手頃なコストで導入できるだけでなく、Reactベースであれば、コピペするだけで地図コンポーネントを導入できてしまうMapTiler CloudとMapLibreの組み合わせで誰でも使えるウェブ地図について紹介したいと思います。

### MapTilerとは

MapTilerはスイス発のウェブ地図サービスです。

MapTiler Cloudのサービスでは、ユーザーは自分が好みの地図、もしくはプロのカートグラファが制作した様々なスタイルの地図をウェブAPIを通して表示することができます。

そして、MapTilerで制作した地図はMapLibre GL JSで簡単にウェブサイトに導入できるので、簡単に手っ取り早くウェブ地図をウェブサイトに導入したい時には時かくおすすめなんです。

### データを整備しよう

では、実際店舗地図を作るにはどうすればいいのか実装してみましょう。

まずは店舗名と店舗の座標が入っているJSONファイルを作成します。

```json
{
    "shops":[
        {
            "name":"創成東店",
            "zahyou":[141.36133592203296,43.06441574854107]
        },
        {
            "name":"札幌駅店",
            "zahyou":[141.3506739162492,43.067693464239895]
        }
    ]
}
```

このステップで注意するする必要があるのは、座標を入れる時は経度を先に入れないといけない点です。

一部の住所を座標に変換するサービスでは緯度が先に生成されるので、入力する前に一度確認してください。

### MapTiler Cloud

次は下に記載しているリンクからMapTiler CloudのHPにアクセスしてください。
[MapTiler Cloud HP](http://maptiler-affiliate-program.sjv.io/BXrxx9)
![スクリーンショット 2022-12-01 16.40.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2706905/26992e69-a1b6-97e9-06de-a99d6d1da16d.png)

右上の青いMy Cloudアイコンからアカウントを作成しますと、地図を読み込む際に使うAPIキーが生成されます。

### いざ、地図コンポーネント

MapTilerのAPIキーが生成されましたら、そのAPIキーを使ってサイトにウェブ地図を埋め込む準備ができました。

まずは地図コンポーネントを導入するために、map.jsファイルを作成してください。

そして、ターミナルからmaplibre GL JSをインストールします。

```bash
npm i maplibre-gl
```

MapLibre GL JS のインストールが終わりましたら、Map.js にライブラリ、店舗リストと今回使うReact JSのフックをインポートします。

```jsx
import maplibreGl from "maplibre-gl";
import 'maplibre-gl/dist/maplibre-gl.css';
const shops = require('./resource/shops.json');
import { useRef, useEffect } from "react";
```

そして、地図コンポーネント用の関数を作成します。
```jsx
export default function Map(props) {

}
```

では、関数の中身を充実して行きましょう！
まずはフックを宣言します。

```jsx
const mapContainer = useRef(null);
const map = useRef(null);
const isInitialMount = useRef(true);
```

次はuseEffect関数を使って地図を初期化します。
```jsx
useEffect( () => {
        if (map.current) {
            return
        }
            map.current = new maplibreGl.Map({
                container: mapContainer.current,
                style: "https://api.maptiler.com/maps/jp-mierune-streets/style.json?key={API key}",
                center: [141.5,43],
                zoom: 8
            })
        },[])
```

styleにはMapTiler Cloud上にある自分のお気に入りの地図スタイルへのリンクをコピペしてください。

ここをコピー↓
![スクリーンショット 2022-12-01 16.55.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2706905/9fe9d9e2-6980-7167-8581-3b5d613c535c.png)

そして、useEffectで店舗のマーカーを地図上に追加します。
```jsx
useEffect(() => {
        if (isInitialMount.current === true) {
            isInitialMount.current = false
            return
        }

        shops.shops.map((val) => {
            // 店舗名でポップアップ作成
            var popup = new maplibreGl.Popup({offset:25}).setText(
                val.name
            );

            // 店舗のマーカーを地図に追加
            new maplibreGl.Marker()
                .setLngLat(val.zahyou)
                .setPopup(popup)
                .addTo(map.current)
        })
    },[])
```

これで、あとはreturn関数で地図コンポーネントをウェブサイトに入れるだけです。
```jsx
return (
        <div ref={mapContainer} style={{height:props.height,width:props.width}}/>
    )
```

全体像ではこうなります：
```jsx
export default function Map(props) {
    const mapContainer = useRef(null);
    const map = useRef(null);
    const isInitialMount = useRef(true);

    // 地図の初期化
    useEffect( () => {
        if (map.current) {
            return
        }
            map.current = new maplibreGl.Map({
                container: mapContainer.current,
                style: "https://api.maptiler.com/maps/jp-mierune-streets/style.json?key={API key}",
                center: [141.5,43],
                zoom: 8
            })
        },[])

    //店舗データを追加
    useEffect(() => {
        if (isInitialMount.current === true) {
            isInitialMount.current = false
            return
        }

        shops.shops.map((val) => {
            // 店舗名でポップアップ作成
            var popup = new maplibreGl.Popup({offset:25}).setText(
                val.name
            );

            // 店舗のマーカーを地図に追加
            new maplibreGl.Marker()
                .setLngLat(val.zahyou)
                .setPopup(popup)
                .addTo(map.current)
        })
    },[])

    return (
        <div ref={mapContainer} style={{height:props.height,width:props.width}}/>
    )
}
```

あとはウェブ地図を導入するページにこのような形でコンポーネントを呼び出すだけで、地図がウェブサイトに導入されます。

```html
<Map height={600} width={600} />
```

地図のサイズを変えたい場合はMapコンポーネントを呼び出す時のheightとwidthを調整してください。

今回紹介している方法で地図を導入しますと、画像のような感じの店舗地図が出来上がります。
![localhost_3000_.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2706905/b86ee2a4-09ca-b304-e193-a0b6bcdacaff.png)

お手頃な価格で簡単に導入できるので、皆さんもぜひ試してみてください。

:::note info
明日の[MIERUNE Advent Calendar 2022](https://qiita.com/advent-calendar/2022/mierune)は@DevKoukiMaedaさんの番です！お楽しみにー
:::