ElixirDesktop APIレス開発 GoogleMapとMapLibreの表示

# はじめに
この記事は Elixirアドベントカレンダーのシリーズ４の20日目の記事です

今回はMapLibreとGoogleMapを表示します

# GoogleMap
GoogleMapの表示方法を解説します
## ライブラリの追加

```
cd assets
npm install @googlemaps/js-api-loader
```

## JS Hookの作成
LiveViewにはJS Hookというものがあって、
JS側のhandleEvent, pushEvent
Elixir側のpushEvent
という関数を使って相互に JSONとMapのやり取りができます

mount時にGoogleMapを表示するようにします
試す場合は GoogleMapAPIのキーを取得してください

```assets/js/hooks.js
import { Loader } from "@googlemaps/js-api-loader";
let Hooks = {};
const apiKey = "your api ky";
Hooks.GoogleMap = {
  mounted() {
    const loader = new Loader({
      apiKey: apiKey,
      version: "weekly",
    });

    loader.load().then(() => {
      const map = new google.maps.Map(document.getElementById("map"), {
        center: { lat: 33.30639, lng: 130.41806 },
        zoom: 9,
      });
      window.map = map;
    });
  },
};

export default Hooks;
```

## JS Hookをセット

mounted()関数が LiveView のmount後に実行されるので、
初期データの作成他に待ち受けるイベントを記述したりします

hooksファイルができたら,liveSocketのoptionsの箇所に hooks: Hooks で追加します

```diff_javascript:assets/app.js
import "phoenix_html"
// Establish Phoenix Socket and LiveView configuration.
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
+ import Hooks from "./hooks"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
- let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
+ let liveSocket = new LiveSocket("/live", Socket, {hooks: Hooks, params: {_csrf_token: csrfToken}})
...

```
## JS Hookの使用してMapの表示

Hooksを読み込む際の注意点ですが
Hooksを読み込むタグにIDをつけて読み込むモジュールをphx-hook="Map"で指定します
また、canvasなどタグ内が頻繁に変化する箇所はLiveViewが変更を検知して再レンダリングされるので、
LiveViewが変更しないようにphx-update="ignore"を指定します

```diff_elixir:lib/spotter_web/live/route_live/show.html.heex
<.gheader title={@route.name}>
...
</.gheader>

- <.list>
-  <:item title="Name"><%= @route.name %></:item>
- </.list>

+ <div 
+  id="map"
+  class="w-screen h-[82vh] -m-4 "
+  phx-update="ignore"
+  phx-hook="GoogleMap"
+ ></div>

<.bottom_tab title="Route" />
...
```

![スクリーンショット 2023-12-21 0.39.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/55e5b1bd-c16f-227f-faf0-7e541d10b722.png)

## MapLibreの表示
MapLibereとはMapBoxからフォークされたWebGLのOSS地図ライブラリです。
GoogleMapとの違いとして地図データを切り替えることができ、OpenStreetMapなどの地図データであればAPIキーの取得なしに無料で使うことができます

https://maplibre.org/maplibre-gl-js/docs/

## ライブラリの追加
CDNから読み込みます

```diff_elixir:lib/spotter_web/components/layouts/root.html.heex
<!DOCTYPE html>
<html lang="en" class="[scrollbar-gutter:stable]">
  <head>
    ...
    <link phx-track-static rel="stylesheet" href={~p"/assets/app.css"} />
+   <link href='https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.css' rel='stylesheet' />
+   <script src='https://unpkg.com/maplibre-gl@2.4.0/dist/maplibre-gl.js'></script>
    <script defer phx-track-static type="text/javascript" src={~p"/assets/app.js"}>
    </script>
  </head>
  <body class="bg-white antialiased">
    <%= @inner_content %>
  </body>
</html>
```

## JS Hookの作成

```assets/js/hooks.js
let Hooks = {};

Hooks.MapLibre = {
  mounted() {
    const map = new maplibregl.Map({
      container: "map",
      style: "https://tile2.openstreetmap.jp/styles/osm-bright/style.json",
      center: { lat: 33.30639, lng: 130.41806 },
      zoom: 8,
    });

    window.map = map;
  },
};

...
```

## JS Hookをセット
GoogleMapの方でしたので省略

## JS Hookの使用してMapの表示

```diff_elixir:lib/spotter_web/live/route_live/show.html.heex
<.gheader title={@route.name}>
...
</.gheader>

- <.list>
-  <:item title="Name"><%= @route.name %></:item>
- </.list>

+ <div 
+  id="map"
+  class="w-screen h-[82vh] -m-4 "
+  phx-update="ignore"
+  phx-hook="MapLibre"
+ ></div>

<.bottom_tab title="Route" />
...
```

![スクリーンショット 2023-12-21 1.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64198/73dfd442-65eb-9e80-0bbc-b472ea409481.png)

# 最後に
LiveView上でGoogleMapとMapLibre両方の表示方法を解説しました
tailwindのお陰で表示範囲の調整が楽になりました

本シリーズはMapLibreで進めていきます
次はLocationAPIを使用して実際にログを取っていきます

本記事は以上になります、ありがとうございました