震度分布図を作ろう⑦ ～観測点を描画しよう～

## はじめ
みなさんこんにちは。
時間がかかりすぎるので、バカみたいに余計なことを書くのをやめました。

### 前回したこと
前回は、更新ボタンの設置やその他諸々の細かい修正を行いました。

### 今回すること
今回はようやく震度分布図の本題、観測点の描画を行っていきます。

## 開発スタート
### 前回までのコード

前回までのコードは

https://qiita.com/kottaro123456/items/8227eadf13da323ed6ec#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB

に貼ってありますので、コピーして利用ください。

### ①観測点の情報を持ってこよう
P2P地震情報のJSONを見ていてわかりますが、JSON内には、<b>「観測点名」「地域かどうか」「所在県名」「観測震度」</b>のみしか書かれていません。

```javascript
  {
    …
    "points": [
      {
        "addr": "宇都宮市明保野町", // 観測点名
        "isArea": false, // 地域かどうか
        "pref": "栃木県", // 所在県名
        "scale": 20 // 観測震度
      },
      {
        "addr": "栃木市岩舟町静",
        "isArea": false,
        "pref": "栃木県",
        "scale": 10
      },
```

しかし、観測点を地図上に描画するということは<b>「座標」</b>が必要です。
そのため、観測点の一覧を持ってくる必要があります。

下のリンクへ飛んで2つのファイルをダウンロードしてください。

https://nanka.cloudfree.jp/webapps/shindobunpu/source/old/JMAstations.json

https://nanka.cloudfree.jp/webapps/shindobunpu/source/old/JMAPoints.js

そして、読み込みます。
`JMAPoints.js`の方はJavaScriptですので、HTMLファイルに直接タグをつけます。

```diff_html:index.html
    …
    </div>
+    <script src="source/JMAPoints.js"></script>
    <script src="index.js"></script>
    …
```

`index.js`内で使用するので、`index.js`読み込み＆実行前に置きます。

また、`JMAstations.json`を読み込みます。

```diff_javascript:index.js
var QuakeJson;
+var JMAPointsJson;
```
まず読み込んだJSONを入れておくための変数を作り

```diff_javascript:index.js
$.getJSON("prefectures.geojson", function (data) {
    L.geoJson(data, {
        style: PolygonLayer_Style_nerv
    }).addTo(map);
});

+$.getJSON("source/JMAstations.json", function (data) {
+    JMAPointsJson = data;
+});
```

読み込んで変数に保存します。

### ②震度アイコンを持ってくる
震度アイコンを持ってきましょう。
自作してもよいですし、下のリンクからダウンロードしてもらっても構いません。

[震度1](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int1.png)　[震度2](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int2.png)　[震度3](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int3.png)　[震度4](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int4.png)　[震度5弱](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int50.png)　[震度5強](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int55.png)　[震度6弱](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int60.png)　[震度6強](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int65.png)　[震度7](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int7.png)　[震度不明](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_int_.png)　[観測点情報なし](https://nanka.cloudfree.jp/webapps/shindobunpu/source/iconOld/png/jqk_intN.png)

マウスのスクロールボタンを押してバックグラウンドタブで開いて一気にダウンロードするのがいいと思います。

HTMLやJSファイルが入っているところに、「source」フォルダを作り、
・index.html
・index.js
┗**source**
　・**jqk_int1.png**
　・**jqk_int2.png**
 のように設置してください。

### ③観測点を描画しよう
まずは全てのコードを乗せておきます。
変更点が結構ありますから全て載せますので、コピペしてください。

<details><summary>ソースコード</summary>

```javascript:index.js
var QuakeJson;
var JMAPointsJson;

var map = L.map('map').setView([36.575, 137.984], 6);
L.control.scale({ maxWidth: 150, position: 'bottomright', imperial: false }).addTo(map);
map.zoomControl.setPosition('topright');

//地図に表示させる上下の順番
map.createPane("pane_map1").style.zIndex = 1; //地図（背景）
map.createPane("pane_map2").style.zIndex = 2; //地図（市町村）
map.createPane("pane_map3").style.zIndex = 3; //地図（細分）
map.createPane("pane_map_filled").style.zIndex = 5; //塗りつぶし
map.createPane("shindo10").style.zIndex = 10;
map.createPane("shindo20").style.zIndex = 20;
map.createPane("shindo30").style.zIndex = 30;
map.createPane("shindo40").style.zIndex = 40;
map.createPane("shindo45").style.zIndex = 45;
map.createPane("shindo46").style.zIndex = 46;
map.createPane("shindo50").style.zIndex = 50;
map.createPane("shindo55").style.zIndex = 55;
map.createPane("shindo60").style.zIndex = 60;
map.createPane("shindo70").style.zIndex = 70;
map.createPane("shingen").style.zIndex = 100; //震源
map.createPane("tsunami_map").style.zIndex = 110; //津波

var PolygonLayer_Style_nerv = {
    "color": "#ffffff",
    "weight": 1.5,
    "opacity": 1,
    "fillColor": "#3a3a3a",
    "fillOpacity": 1
}

$.getJSON("prefectures.geojson", function (data) {
    L.geoJson(data, {
        pane: "pane_map3",
        style: PolygonLayer_Style_nerv
    }).addTo(map);
});

$.getJSON("source/JMAstations.json", function (data) {
    JMAPointsJson = data;
    GetQuake();
});

//ボタン押下時のイベント設定とローカルストレージの設定
document.getElementById('reload').addEventListener("click",()=>{
    if (document.getElementById('reload_num').value != "") {
        if (document.getElementById('reload_num').value > 100 || document.getElementById('reload_num').value <= 0) {
            GetQuake(100);
        } else {
            GetQuake(document.getElementById('reload_num').value);
        }
    } else {
        GetQuake();
    }
    document.getElementById('reload').innerText = "更新中…";
    setTimeout(() => {
        document.getElementById('reload').innerText = "更新完了";
        setTimeout(() => {
            document.getElementById('reload').innerText = "情報更新";
        }, 1000);
    }, 1000);
});

function GetQuake(option) {
    var url;
    if (!isNaN(option)) {
        url = "https://api.p2pquake.net/v2/history?codes=551&limit="+option;
    } else {
        url = "https://api.p2pquake.net/v2/history?codes=551&limit=20";
    }
    $.getJSON(url, function (data) {
        QuakeJson = data;

        while (document.getElementById('quakelist').lastChild) {
            document.getElementById('quakelist').removeChild(document.getElementById('quakelist').lastChild);
        }
    
        var forEachNum = 0;
        data.forEach(element => {
            var option = document.createElement("option");
            var text;
            let maxInt_data = element['earthquake']['maxScale'];
            let maxIntText = hantei_maxIntText(maxInt_data);
            let Name = hantei_Name(element['earthquake']['hypocenter']['name']);
            let Time = element['earthquake']['time'];
            if (element["issue"]["type"] == "ScalePrompt") {
                text = "【震度速報】" + element["points"][0]["addr"] + "など " + "\n" + Time.slice(0, -3) + "\n最大震度 : " + maxIntText;
            } else if (element["issue"]["type"] == "Foreign") {
                text = "【遠地地震】" + Time.slice(0, -3) + " " + Name;
            } else {
                text = Time.slice(0, -3) + " " + Name + " " +  "\n" + "\n最大震度 : " + maxIntText;
            }
            option.value = "" + forEachNum + "";
            option.textContent = text;
            document.getElementById('quakelist').appendChild(option);
            forEachNum++;
        });
    
        //地震情報リストをクリックしたときの発火イベント
        var list = document.getElementById('quakelist');
        list.onchange = event => {
            QuakeSelect(list.selectedIndex);
        }
        
        QuakeSelect(0);
    });
}

var shingenIcon;
var shindo_icon;
var shindo_layer = L.layerGroup();
function QuakeSelect(num) {
    if (shingenIcon && shindo_layer) {
        map.removeLayer(shingenIcon);
        map.removeLayer(shindo_layer);
        shingenIcon = "";
        shindo_layer = L.layerGroup();
        shindo_icon = "";
    }
    let maxInt_data = QuakeJson[num]['earthquake']['maxScale'];
    var maxIntText = hantei_maxIntText(maxInt_data);
    var Magnitude = hantei_Magnitude(QuakeJson[num]['earthquake']['hypocenter']['magnitude']);
    var Name = hantei_Name(QuakeJson[num]['earthquake']['hypocenter']['name']);
    var Depth = hantei_Depth(QuakeJson[num]['earthquake']['hypocenter']['depth']);
    var tsunamiText = hantei_tsunamiText(QuakeJson[num]['earthquake']['domesticTsunami']);
    var Time = QuakeJson[num]['earthquake']['time'];

    var shingenLatLng = new L.LatLng(QuakeJson[num]["earthquake"]["hypocenter"]["latitude"], QuakeJson[num]["earthquake"]["hypocenter"]["longitude"]);
    var shingenIconImage = L.icon({
        iconUrl: 'source/shingen.png',
        iconSize: [40, 40],
        iconAnchor: [20, 20],
        popupAnchor: [0, -40]
    });
    shingenIcon = L.marker(shingenLatLng, {icon: shingenIconImage }).addTo(map);
    shingenIcon.bindPopup('発生時刻：'+Time+'<br>最大震度：'+maxIntText+'<br>震源地：'+Name+'<span style=\"font-size: 85%;\"> ('+QuakeJson[num]["earthquake"]["hypocenter"]["latitude"]+", "+QuakeJson[num]["earthquake"]["hypocenter"]["longitude"]+')</span><br>規模：M'+Magnitude+'　深さ：'+Depth+'<br>受信：'+QuakeJson[num]['issue']['time']+', '+QuakeJson[num]['issue']['source'],{closeButton: false, zIndexOffset: 10000, maxWidth: 10000});
    shingenIcon.on('mouseover', function (e) {this.openPopup();});
    shingenIcon.on('mouseout', function (e) {this.closePopup();});

    if (QuakeJson[num]["issue"]["type"] != "ScalePrompt") { //各地の震度に関する情報
        //観測点の震度についてすべての観測点に対して繰り返す
        QuakeJson[num]["points"].forEach(element => {
        var result = JMAPoints.indexOf(element["addr"]);
        if (result != -1) {
            var ImgUrl = "";
            var PointShindo = "";
            var icon_theme = "jqk";
            if (element["scale"] == 10) {
                ImgUrl = "source/"+icon_theme+"_int1.png";
                PointShindo = "震度1";
            } else if (element["scale"] == 20) {
                ImgUrl = "source/"+icon_theme+"_int2.png";
                PointShindo = "震度2";
            } else if (element["scale"] == 30) {
                ImgUrl = "source/"+icon_theme+"_int3.png";
                PointShindo = "震度3";
            } else if (element["scale"] == 40) {
                ImgUrl = "source/"+icon_theme+"_int4.png";
                PointShindo = "震度4";
            } else if (element["scale"] == 45) {
                ImgUrl = "source/"+icon_theme+"_int50.png";
                PointShindo = "震度5弱";
            } else if (element["scale"] == 46) {
                ImgUrl = "source/"+icon_theme+"_int_.png";
                PointShindo = "震度5弱以上と推定";
            } else if (element["scale"] == 50) {
                ImgUrl = "source/"+icon_theme+"_int55.png";
                PointShindo = "震度5強";
            } else if (element["scale"] == 55) {
                ImgUrl = "source/"+icon_theme+"_int60.png";
                PointShindo = "震度6弱";
            } else if (element["scale"] == 60) {
                ImgUrl = "source/"+icon_theme+"_int65.png";
                PointShindo = "震度6強";
            } else if (element["scale"] == 70) {
                ImgUrl = "source/"+icon_theme+"_int7.png";
                PointShindo = "震度7";
            } else {
                ImgUrl = "source/"+icon_theme+"_int_.png";
                PointShindo = "震度不明";
            }
            if (element["isArea"] == false) { //観測点
                let shindo_latlng = new L.LatLng(JMAPointsJson[result]["lat"], JMAPointsJson[result]["lon"]);
                let shindoIcon = L.icon({
                    iconUrl: ImgUrl,
                    iconSize: [20, 20],
                    popupAnchor: [0, -40]
                });
                let shindoIcon_big = L.icon({
                    iconUrl: ImgUrl,
                    iconSize: [34, 34],
                    popupAnchor: [0, -40]
                });
                shindo_icon = L.marker(shindo_latlng, { icon: shindoIcon,pane: eval('\"shindo'+element["scale"]+'\"') });
                shindo_icon.bindPopup('<ruby>'+element["addr"] + '<rt style="font-size: 0.7em;">' + JMAPointsJson[result]["furigana"] + '</rt></ruby>　'+ PointShindo,{closeButton: false, zIndexOffset: 10000,autoPan: false,});
                shindo_icon.on('mouseover', function (e) {
                    this.openPopup();
                });
                shindo_icon.on('mouseout', function (e) {
                    this.closePopup();
                });
                shindo_layer.addLayer(shindo_icon);
            }
        }
        });
    }
    map.addLayer(shindo_layer);
    map.flyTo(shingenLatLng, 7.5, { duration: 0.5 })
}


function hantei_maxIntText(param) {
    let kaerichi = param == 10 ? "1" : param == 20 ? "2" : param == 30 ? "3" : param == 40 ? "4" :
    param == 45 ? "5弱" : param == 46 ? "5弱" : param == 50 ? "5強" : param == 55 ? "6弱" :
    param == 60 ? "6強" : param == 70 ? "7" : "不明";
    return kaerichi;
}
function hantei_Magnitude(param) {
    let kaerichi = param != -1 ? param.toFixed(1) : 'ー.ー';
    return kaerichi;
}
function hantei_Name(param) {
    let kaerichi = param != "" ? param : '情報なし';
    return kaerichi;
}
function hantei_Depth(param) {
    let kaerichi = param != -1 ? "約"+param+"km" : '不明';
    return kaerichi;
}
function hantei_tsunamiText(param) {
    let kaerichi = param == "None" ? "なし" :
    param == "Unknown" ? "不明" :
    param == "Checking" ? "調査中" :
    param == "NonEffective" ? "若干の海面変動" :
    param == "Watch" ? "津波注意報" :
    param == "Warning" ? "津波警報" : "情報なし";
    return kaerichi;
}
```

</details>

では解説です。

```javascript:index.js :8 (※数字は行番号です。)
//地図に表示させる上下の順番
map.createPane("pane_map1").style.zIndex = 1; //地図（背景）
map.createPane("pane_map2").style.zIndex = 2; //地図（市町村）
map.createPane("pane_map3").style.zIndex = 3; //地図（細分）
map.createPane("pane_map_filled").style.zIndex = 5; //塗りつぶし
map.createPane("shindo10").style.zIndex = 10;
```
なんだこれはと思われると思いますが、これは`pane`といってオブジェクトを乗っける板のようなものです。
今回ではそれを重ね順をコントロールするために使用するべく、`z-index`でz軸（上下方向）の高さを設定しています。（数字の大きいほうが上に来ます。）

![panesetsumei.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637649/55da306a-dfb6-09de-891e-756103b1e0a1.png)
こんな感じですね。（ブラウザが重すぎるので作ってみた）

```diff_javascript:index.js :41
$.getJSON("source/JMAstations.json", function (data) {
    JMAPointsJson = data;
+    GetQuake();
});
```
これで`JMAstations.json`、つまり観測点情報一覧を読み込んだ後に地震取得＆描画を実行しています。
このファイルは重いので、描画を実行する前に読み込まれずエラーとなるのを防ぐため、読み込みが完了したあとで、地震取得の関数を実行しています。

```diff_javascript:index.js :114
function QuakeSelect(num) {
    if (shingenIcon && shindo_layer) {
        map.removeLayer(shingenIcon);
        map.removeLayer(shindo_layer);
        shingenIcon = "";
        shindo_layer = L.layerGroup();
        shindo_icon = "";
    }
```
ここで`shingenIcon`と`shindo_layer`が両方とも存在した場合にマップ上から震源や震度アイコンを削除しています。

```diff_javascript:index.js :141
if (QuakeJson[num]["issue"]["type"] != "ScalePrompt") { //各地の震度に関する情報
    //観測点の震度についてすべての観測点に対して繰り返す
    QuakeJson[num]["points"].forEach(element => {
```
P2P地震情報では`QuakeJson[num]["issue"]["type"]`プロパティで「震度分布」か「震度速報(おおまかな地域ごとの震度を地震発生後すぐに伝える)」かがわかります。
震度速報の描画は少しめんどくさいので後に回すとしましょう。
また、`.forEach(element => {`ですべての観測点に対して繰り返して観測点描画を実行しています。
`forEach`の解説は[こちらの⑤](https://qiita.com/kottaro123456/items/99bdc867ae7e54a90acb#%E3%83%AA%E3%82%B9%E3%83%88%E3%81%AB%E5%9C%B0%E9%9C%87%E6%83%85%E5%A0%B1%E3%82%92%E8%BF%BD%E5%8A%A0)をご覧ください。

```diff_javascript:index.js :144
var result = JMAPoints.indexOf(element["addr"]);
```
`indexOf`というのは文字列を検索する機能ですが、配列に対しても実行できます。
返り値は配列の番号と一致しているので、そのまま`result`を使用して観測点の情報を呼び出すことができます。

```diff_javascript:index.js :145
if (result != -1) {
    var ImgUrl = "";
    var PointShindo = "";
    var icon_theme = "jqk";
    if (element["scale"] == 10) {
        ImgUrl = "source/"+icon_theme+"_int1.png";
        PointShindo = "震度1";
```
`indexOf`で探している値が見つからなかった場合`-1`を返すことを利用して、`result`が`-1`で<b>ないとき</b>で観測点描画を実行しています。
また、その下ではマーカーアイコン画像の画像URLの生成や震度◯と日本語に直したりする条件分岐が続きます。

```diff_javascript:index.js :183
if (element["isArea"] == false) { //観測点
```
でここの観測点が地域塗りつぶしの対象ではないことを確認しています。
（いつか地域を塗りつぶしするときにも観測点が地域かどうかの情報は使いません。）

```diff_javascript:index.js :184
let shindo_latlng = new L.LatLng(JMAPointsJson[result]["lat"], JMAPointsJson[result]["lon"]);
let shindoIcon = L.icon({
    iconUrl: ImgUrl,
    iconSize: [20, 20],
    popupAnchor: [0, -40]
});
```
震度アイコンの位置座標を引っ張ってきています。
また、震度アイコンのスタイルを設定しています。

```diff_javascript:index.js :195
shindo_icon = L.marker(shindo_latlng, { icon: shindoIcon,pane: eval('\"shindo'+element["scale"]+'\"') });
shindo_icon.bindPopup('<ruby>'+element["addr"] + '<rt style="font-size: 0.7em;">' + JMAPointsJson[result]["furigana"] + '</rt></ruby>　'+ PointShindo,{closeButton: false, zIndexOffset: 10000,autoPan: false,});
shindo_icon.on('mouseover', function (e) {
    this.openPopup();
});
shindo_icon.on('mouseout', function (e) {
    this.closePopup();
});
```
ここでマーカーを作成し、それに付随するポップアップの内容も生成しています。

```diff_javascript:index.js :203
shindo_layer.addLayer(shindo_icon);
```
ここで一旦`shindo_layer`という入れ物にアイコンを格納しておきます。
なぜこうするかというと、後々削除するときに入れ物ごと削除してしまえば、個々のアイコンについて処理をしなくても良くなるからです。
`pane`と`layer`の違いといえば、`pane`は単純な板みたいなものであるのに対し、`layer`は削除したりできる箱みたいなものというべきでしょうか。とりあえず操作性は`layer`が勝ちますね。

```diff_javascript:index.js :208
map.addLayer(shindo_layer);
```
ここで先ほど震度アイコンをまとめて入れていた`shindo_layer`という入れ物を地図に追加します。

## 終わりに
今回は肝である機能、観測点の描画をやっていきました。
結構難しそうに見えて、解説を読んでいったら実はまあまあ簡単だった。みたいな感じに捉えてくれているでしょうか。
まあ「なんだこれは。なにの処理をしているのか。」や「なぜこの処理で行けるのか。」など疑問がありましたらコメントをどしどしお寄せください。
次回では観測点の描画周りをもう少し改善していきましょう。

今回のサンプル
[https://nanka.cloudfree.jp/bin/webapps/shindobunpu_qiita/7/](https://nanka.cloudfree.jp/bin/webapps/shindobunpu_qiita/7/)

<details><summary>今回のソースコード</summary>

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <title>震度分布図</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
    <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
    <script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://nanka.cloudfree.jp/static/font/fonts.css">
    <link rel="stylesheet" href="index.css">
</head>
<body>
    <div id="map"></div>
    <div class="btns">
        <select id="quakelist"><option>地震情報の取得中…</option></select>
        <span class="setsumei" style="padding: 0;padding-right: 5px;"><button id="reload">情報更新</button> <input type="number" id="reload_num" value="20" max="100">件</span>
    </div>
    <script src="source/JMAPoints.js"></script>
    <script src="index.js"></script>
</body>
</html>
```

```javascript:index.js
var QuakeJson;
var JMAPointsJson;

var map = L.map('map').setView([36.575, 137.984], 6);
L.control.scale({ maxWidth: 150, position: 'bottomright', imperial: false }).addTo(map);
map.zoomControl.setPosition('topright');

//地図に表示させる上下の順番
map.createPane("pane_map1").style.zIndex = 1; //地図（背景）
map.createPane("pane_map2").style.zIndex = 2; //地図（市町村）
map.createPane("pane_map3").style.zIndex = 3; //地図（細分）
map.createPane("pane_map_filled").style.zIndex = 5; //塗りつぶし
map.createPane("shindo10").style.zIndex = 10;
map.createPane("shindo20").style.zIndex = 20;
map.createPane("shindo30").style.zIndex = 30;
map.createPane("shindo40").style.zIndex = 40;
map.createPane("shindo45").style.zIndex = 45;
map.createPane("shindo46").style.zIndex = 46;
map.createPane("shindo50").style.zIndex = 50;
map.createPane("shindo55").style.zIndex = 55;
map.createPane("shindo60").style.zIndex = 60;
map.createPane("shindo70").style.zIndex = 70;
map.createPane("shingen").style.zIndex = 100; //震源
map.createPane("tsunami_map").style.zIndex = 110; //津波

var PolygonLayer_Style_nerv = {
    "color": "#ffffff",
    "weight": 1.5,
    "opacity": 1,
    "fillColor": "#3a3a3a",
    "fillOpacity": 1
}

$.getJSON("prefectures.geojson", function (data) {
    L.geoJson(data, {
        pane: "pane_map3",
        style: PolygonLayer_Style_nerv
    }).addTo(map);
});

$.getJSON("source/JMAstations.json", function (data) {
    JMAPointsJson = data;
    GetQuake();
});

//ボタン押下時のイベント設定とローカルストレージの設定
document.getElementById('reload').addEventListener("click",()=>{
    if (document.getElementById('reload_num').value != "") {
        if (document.getElementById('reload_num').value > 100 || document.getElementById('reload_num').value <= 0) {
            GetQuake(100);
        } else {
            GetQuake(document.getElementById('reload_num').value);
        }
    } else {
        GetQuake();
    }
    document.getElementById('reload').innerText = "更新中…";
    setTimeout(() => {
        document.getElementById('reload').innerText = "更新完了";
        setTimeout(() => {
            document.getElementById('reload').innerText = "情報更新";
        }, 1000);
    }, 1000);
});

function GetQuake(option) {
    var url;
    if (!isNaN(option)) {
        url = "https://api.p2pquake.net/v2/history?codes=551&limit="+option;
    } else {
        url = "https://api.p2pquake.net/v2/history?codes=551&limit=20";
    }
    $.getJSON(url, function (data) {
        QuakeJson = data;

        while (document.getElementById('quakelist').lastChild) {
            document.getElementById('quakelist').removeChild(document.getElementById('quakelist').lastChild);
        }
    
        var forEachNum = 0;
        data.forEach(element => {
            var option = document.createElement("option");
            var text;
            let maxInt_data = element['earthquake']['maxScale'];
            let maxIntText = hantei_maxIntText(maxInt_data);
            let Name = hantei_Name(element['earthquake']['hypocenter']['name']);
            let Time = element['earthquake']['time'];
            if (element["issue"]["type"] == "ScalePrompt") {
                text = "【震度速報】" + element["points"][0]["addr"] + "など " + "\n" + Time.slice(0, -3) + "\n最大震度 : " + maxIntText;
            } else if (element["issue"]["type"] == "Foreign") {
                text = "【遠地地震】" + Time.slice(0, -3) + " " + Name;
            } else {
                text = Time.slice(0, -3) + " " + Name + " " +  "\n" + "\n最大震度 : " + maxIntText;
            }
            option.value = "" + forEachNum + "";
            option.textContent = text;
            document.getElementById('quakelist').appendChild(option);
            forEachNum++;
        });
    
        //地震情報リストをクリックしたときの発火イベント
        var list = document.getElementById('quakelist');
        list.onchange = event => {
            QuakeSelect(list.selectedIndex);
        }
        
        QuakeSelect(0);
    });
}

var shingenIcon;
var shindo_icon;
var shindo_layer = L.layerGroup();
function QuakeSelect(num) {
    if (shingenIcon && shindo_layer) {
        map.removeLayer(shingenIcon);
        map.removeLayer(shindo_layer);
        shingenIcon = "";
        shindo_layer = L.layerGroup();
        shindo_icon = "";
    }
    let maxInt_data = QuakeJson[num]['earthquake']['maxScale'];
    var maxIntText = hantei_maxIntText(maxInt_data);
    var Magnitude = hantei_Magnitude(QuakeJson[num]['earthquake']['hypocenter']['magnitude']);
    var Name = hantei_Name(QuakeJson[num]['earthquake']['hypocenter']['name']);
    var Depth = hantei_Depth(QuakeJson[num]['earthquake']['hypocenter']['depth']);
    var tsunamiText = hantei_tsunamiText(QuakeJson[num]['earthquake']['domesticTsunami']);
    var Time = QuakeJson[num]['earthquake']['time'];

    var shingenLatLng = new L.LatLng(QuakeJson[num]["earthquake"]["hypocenter"]["latitude"], QuakeJson[num]["earthquake"]["hypocenter"]["longitude"]);
    var shingenIconImage = L.icon({
        iconUrl: 'source/shingen.png',
        iconSize: [40, 40],
        iconAnchor: [20, 20],
        popupAnchor: [0, -40]
    });
    shingenIcon = L.marker(shingenLatLng, {icon: shingenIconImage }).addTo(map);
    shingenIcon.bindPopup('発生時刻：'+Time+'<br>最大震度：'+maxIntText+'<br>震源地：'+Name+'<span style=\"font-size: 85%;\"> ('+QuakeJson[num]["earthquake"]["hypocenter"]["latitude"]+", "+QuakeJson[num]["earthquake"]["hypocenter"]["longitude"]+')</span><br>規模：M'+Magnitude+'　深さ：'+Depth+'<br>受信：'+QuakeJson[num]['issue']['time']+', '+QuakeJson[num]['issue']['source'],{closeButton: false, zIndexOffset: 10000, maxWidth: 10000});
    shingenIcon.on('mouseover', function (e) {this.openPopup();});
    shingenIcon.on('mouseout', function (e) {this.closePopup();});

    if (QuakeJson[num]["issue"]["type"] != "ScalePrompt") { //各地の震度に関する情報
        //観測点の震度についてすべての観測点に対して繰り返す
        QuakeJson[num]["points"].forEach(element => {
        var result = JMAPoints.indexOf(element["addr"]);
        if (result != -1) {
            var ImgUrl = "";
            var PointShindo = "";
            var icon_theme = "jqk";
            if (element["scale"] == 10) {
                ImgUrl = "source/"+icon_theme+"_int1.png";
                PointShindo = "震度1";
            } else if (element["scale"] == 20) {
                ImgUrl = "source/"+icon_theme+"_int2.png";
                PointShindo = "震度2";
            } else if (element["scale"] == 30) {
                ImgUrl = "source/"+icon_theme+"_int3.png";
                PointShindo = "震度3";
            } else if (element["scale"] == 40) {
                ImgUrl = "source/"+icon_theme+"_int4.png";
                PointShindo = "震度4";
            } else if (element["scale"] == 45) {
                ImgUrl = "source/"+icon_theme+"_int50.png";
                PointShindo = "震度5弱";
            } else if (element["scale"] == 46) {
                ImgUrl = "source/"+icon_theme+"_int_.png";
                PointShindo = "震度5弱以上と推定";
            } else if (element["scale"] == 50) {
                ImgUrl = "source/"+icon_theme+"_int55.png";
                PointShindo = "震度5強";
            } else if (element["scale"] == 55) {
                ImgUrl = "source/"+icon_theme+"_int60.png";
                PointShindo = "震度6弱";
            } else if (element["scale"] == 60) {
                ImgUrl = "source/"+icon_theme+"_int65.png";
                PointShindo = "震度6強";
            } else if (element["scale"] == 70) {
                ImgUrl = "source/"+icon_theme+"_int7.png";
                PointShindo = "震度7";
            } else {
                ImgUrl = "source/"+icon_theme+"_int_.png";
                PointShindo = "震度不明";
            }
            if (element["isArea"] == false) { //観測点
                let shindo_latlng = new L.LatLng(JMAPointsJson[result]["lat"], JMAPointsJson[result]["lon"]);
                let shindoIcon = L.icon({
                    iconUrl: ImgUrl,
                    iconSize: [20, 20],
                    popupAnchor: [0, -40]
                });
                let shindoIcon_big = L.icon({
                    iconUrl: ImgUrl,
                    iconSize: [34, 34],
                    popupAnchor: [0, -40]
                });
                shindo_icon = L.marker(shindo_latlng, { icon: shindoIcon,pane: eval('\"shindo'+element["scale"]+'\"') });
                shindo_icon.bindPopup('<ruby>'+element["addr"] + '<rt style="font-size: 0.7em;">' + JMAPointsJson[result]["furigana"] + '</rt></ruby>　'+ PointShindo,{closeButton: false, zIndexOffset: 10000,autoPan: false,});
                shindo_icon.on('mouseover', function (e) {
                    this.openPopup();
                });
                shindo_icon.on('mouseout', function (e) {
                    this.closePopup();
                });
                shindo_layer.addLayer(shindo_icon);
            }
        }
        });
    }
    map.addLayer(shindo_layer);
    map.flyTo(shingenLatLng, 7.5, { duration: 0.5 })
}


function hantei_maxIntText(param) {
    let kaerichi = param == 10 ? "1" : param == 20 ? "2" : param == 30 ? "3" : param == 40 ? "4" :
    param == 45 ? "5弱" : param == 46 ? "5弱" : param == 50 ? "5強" : param == 55 ? "6弱" :
    param == 60 ? "6強" : param == 70 ? "7" : "不明";
    return kaerichi;
}
function hantei_Magnitude(param) {
    let kaerichi = param != -1 ? param.toFixed(1) : 'ー.ー';
    return kaerichi;
}
function hantei_Name(param) {
    let kaerichi = param != "" ? param : '情報なし';
    return kaerichi;
}
function hantei_Depth(param) {
    let kaerichi = param != -1 ? "約"+param+"km" : '不明';
    return kaerichi;
}
function hantei_tsunamiText(param) {
    let kaerichi = param == "None" ? "なし" :
    param == "Unknown" ? "不明" :
    param == "Checking" ? "調査中" :
    param == "NonEffective" ? "若干の海面変動" :
    param == "Watch" ? "津波注意報" :
    param == "Warning" ? "津波警報" : "情報なし";
    return kaerichi;
}
```

```css:index.css
html, body, #map {
    width: 100%;
    height: 100%;
    margin: 0;
}
#map {
    background: #1d1d1d;
}

.btns {
    position: absolute;
    bottom: 10px;
    left: 10px;
    z-index: 10000;
    user-select: none;
}
.setsumei, #quakelist, #reload, #map_ichi, #test, #btn_shindo_ichiran, #autoreload, #display_onoff_point_check, #view_info {
    display: inline-block;
    background: #00000088;
    border: white 2.5px solid;
    border-radius: 5px;
    color: white;
    padding: 5px;
    font-family: "ヒラギノ角ゴ-Pro",'Noto Sans JP';
    font-weight: 500;
    font-size: 0.8rem;
    cursor: pointer;
}
#reload, #autoreload {
    background: #00000000;
    padding: 5px;
    border: none;
}
#reload_num, #autoreload_num {
    background: #00000000;
    border: 0;
    width: 2rem;
    font-family: "ヒラギノ角ゴ-Pro",'Noto Sans JP';
    font-weight: 500;
    font-size: 0.9rem;
    color: white;
    border-bottom: #ffffff 1px solid;
}
select, input, button {
    outline: none;
}
input[type="number"]::-webkit-outer-spin-button, 
input[type="number"]::-webkit-inner-spin-button { 
    -webkit-appearance: none; 
    margin: 0; 
} 
input[type="number"] { 
    -moz-appearance:textfield; 
} 
.setsumei {
    cursor: default;
}
#quakelist {
    width: 25em;
    height: calc(2em + 5px);
}

.leaflet-fade-anim .leaflet-popup {
    transition: 0s;
}
.leaflet-popup-content-wrapper {
	background-color: rgba(255, 255, 255, 0.85);
    box-shadow: 0px 3px 7px 2px rgba(0, 0, 0, 0.4);
    border-radius: 0!important;
    font-family: "ヒラギノ角ゴ-Pro",'Noto Sans JP';
    font-weight: 500;
    user-select: none;
	margin-bottom: 0;
}
.leaflet-popup-content {
    color: black !important;
    font-size: 1.2rem;
    margin: 10px 8px 8px 8px;
}
.leaflet-popup-tip-container {
    display: none;
}
```

</details>

## 次回予告

https://qiita.com/kottaro123456/items/6d5719347801154f03db