【Rails7】Leafletでサイトに地図を埋め込む

## はじめに

### Leafletとは
Leaflet とは、インタラクティブな地図をWebページに埋め込むためオープンソースJSライブラリです。非常に軽量で、基本的な機能であれば実現できるのが特徴です。

ただし 「**ヘビーユースは禁止**」されているなど、タイル利用規約が規定されているので、ご利用の際はそれに従うようにしてください。

https://wiki.openstreetmap.org/wiki/JA:%E3%82%BF%E3%82%A4%E3%83%AB%E5%88%A9%E7%94%A8%E8%A6%8F%E7%B4%84

## 環境
Ruby 3.1.4
Rails 7.1.0
importmap + stimulus
## 実装

### Gemfileの追加
```diff_ruby:Gemfile
gem 'leaflet-rails'
gem 'importmap-rails'
```

### importmapの設定

下記コマンドでimportmapに関連するファイルを生成します。
```
./bin/rails importmap:install
```

#### 変更されたファイル

```diff_javascript:app/assets/config/manifest.js
//= link_tree ../images
//= link_directory ../stylesheets .css
+ //= link_tree ../../javascript .js
+ //= link_tree ../../../vendor/javascript .js
```

```diff_javascript:app/javascript/application.js
+ // Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
```

```diff_ruby:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>Mapping</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
+    <%= javascript_importmap_tags %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

```diff_ruby:bin/importmap
+ #!/usr/bin/env ruby
+ 
+ require_relative "../config/application"
+ require "importmap/commands"
```

```diff_ruby:config/importmap.rb
+ # Pin npm packages by running ./bin/importmap
+ 
+ pin "application", preload: true
```

### stimulusの設定

こちらは`importmap`を使用する際の環境構築の手順がREADMEにまとめられているため、それ通りに設定していきます。

https://github.com/hotwired/stimulus-rails#with-import-map

具体的には、下記コマンドで`stimulus`に関連するファイルを生成します。

```
./bin/rails stimulus:install
```

#### 変更されたファイル

```diff_javascript:app/javascript/application.js
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
+ import "controllers"
```
```diff_javascript:app/javascript/controllers/application.js
+ import { Application } from "@hotwired/stimulus"
+ 
+ const application = Application.start()
+ 
+ // Configure Stimulus development experience
+ application.debug = false
+ window.Stimulus   = application
+ 
+ export { application }
```
```diff_javascript:app/javascript/controllers/index.js
+ // Import and register all your controllers from the importmap under controllers/*
+ 
+ import { application } from "controllers/application"
+ 
+ // Eager load all controllers defined in the import map under + controllers/**/*_controller
+ import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
+ eagerLoadControllersFrom("controllers", application)
+ 
+ // Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
+ // import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
+ // lazyLoadControllersFrom("controllers", application)
```
```diff_javascript:app/javascript/controllers/hello_controller.js
+ import { Controller } from "@hotwired/stimulus"
+ 
+ export default class extends Controller {
+   connect() {
+     this.element.textContent = "Hello World!"
+   }
+ }
```
```diff_ruby:config/importmap.rb
# Pin npm packages by running ./bin/importmap

pin "application", preload: true
+ pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
+ pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
+ pin_all_from "app/javascript/controllers", under: "controllers"
```
上記で`importmap` + `stimulus`の設定は完了になります。

### Leafletの設定
それでは本題のLeafletの設定に入っていきます。
まずは下記コマンドで、LeafletとLeaflet-cssをピン留めします。
```
./bin/importmap pin leaflet
./bin/importmap pin leaflet-css
```

#### 変更されたファイル
```diff_ruby:config/importmap.rb
# Pin npm packages by running ./bin/importmap

pin "application", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
+ pin "leaflet", to: "https://ga.jspm.io/npm:leaflet@1.9.4/dist/leaflet-src.js"
+ pin "leaflet-css", to: "https://ga.jspm.io/npm:leaflet-css@0.1.0/dist/leaflet.css.min.js"
```
これでLeafletを使用することができるようになります。

#### stimulus controller作成
次に`stimulus`のcontrollerを作成していきます。
中ではライフサイクルコールバックメソッドの、`connect()` `disconnect()`を定義します。

https://stimulus.hotwired.dev/reference/lifecycle-callbacks

```diff_javascript:app/jacascript/controllers/map_controller.js
import { Controller } from "@hotwired/stimulus";
import "leaflet-css";


export default class extends Controller {
    static targets = ["placeholder"]
    connect() {
        import("leaflet").then(L => {
            this.map = L.map(this.placeholderTarget).setView([35.694004, 139.753632], 10);
            L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                maxZoom: 20,
                attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
            }).addTo(this.map);
        });
    }

    disconnect() {
        this.map.remove()
    }
}
```

最後に下記コードをviewに組み込むことで、地図を表示することができます。
```diff_ruby
<div class="leaflet-container" data-controller="map" data-target="map.placeholder" style="height: 500px; width: 500px;"></div>
```

![map.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/838613/ce22d18a-cf45-fae6-f241-7cd88f71a094.png)

## 参考

https://leafletjs.com/index.html