はじめ
みなさんこんにちは。
このシリーズもやっと5回まで来ましたが、どうも没になりそうな更新頻度です…
やばいです
↑結局続きを見たい!と思ってくれた方にコメントしていただけるまで全然更新していませんでした…。
前回したこと
前回④ではなにをしましたっけ。
~震源を表示させよう~ なのでマップ上に震源アイコンを表示したんですよね。
今回すること
今回することとしては、地震を選択可能にしたいと思います。
↑これ
開発スタート
前回までのコードは
前回までのコードはこちら
①リストのHTML要素追加
まず、先ほどの画像の、押したらずらっと出てきて選べるやつ(語彙力)のHTML要素を追加しましょう。
ソースコード
<body>
<div id="map"></div>
+ <div class="btns">
+ <select id="quakelist"><option>地震情報の取得中…</option></select>
+ </div>
<script src="index.js"></script>
</body>
↓CSSはすべて追加してください。
バカみたいにクラス名がありますが、後々いるかもしれないので…。
.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;
}
#quakelist {
width: 25em;
height: calc(2em + 5px);
}
②リストの中身全削除
さあ、地震情報追加だー!!と思いますが、まずは「地震情報の取得中…」を消さないと
となってしまいます。
不細工です。
なので、まずリストの中身を全部消しましょう。
ソースコード
$.getJSON("https://api.p2pquake.net/v2/history?codes=551", function (data) {
+ while (document.getElementById('quakelist').lastChild) {
+ document.getElementById('quakelist').removeChild(document.getElementById('quakelist').lastChild);
+ }
これは、document.getElementById('quakelist')
のlastChild
=一番後ろの要素がある限り永遠に繰り返すということで、
一番後ろの要素があれば、removeChild
で削除しています。
③リストに地震情報を追加
やっと本題です。
地震情報を追加しましょう。
とりあえず、ソースコードを載せます。
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 forEachNum
でこれが何回目のループかを計測しています。あとで使います。
data.forEach(element => {}
ですが、forEachというのは配列をそれぞれ一回ずつ繰り返してくれる大変便利な関数です。
element
にその繰り返している配列の中身が格納されます。
[0, 1, 2, 3]
ならelement = 1
等で、[["data": 123],["data": 987]]
ならelement = ["data": 123]
等となります。
こちらを引用させてもらいます。
とりあえず便利です。何回か試してみて自分で感覚をつかむと配列とかのJSONは無敵です。
var option
でoptionというHTMLの要素を作ります。
let maxInt_data = element['earthquake']['maxScale'];
で震度の10
とか55
とかを、
let maxIntText = hantei_maxIntText(maxInt_data);
で55
とかを6弱
に直します。
if (element["issue"]["type"] == "ScalePrompt") {
で震度速報なのか、普通の震度分布なのか1を判定し、適切な文字列に結合します。
option.value = "" + forEachNum + "";
で、選んだ時に帰ってくる数字value
の値を設定しています。
option.textContent = text;
で見える部分のところの文字列を設定しています。
document.getElementById('quakelist').appendChild(option);
で要素をリストに追加して、
最後に数字を一つ足して、また次へループという感じです。
④選択時に地震情報を再描画
さて、先程まででリストに地震情報を追加し、選択可能になったと思います。
選んだままでは何も起こりませんので、選んだときに地図を更新できるようにしましょう。
…
document.getElementById('quakelist').appendChild(option);
forEachNum++;
});
+ //地震情報リストをクリックしたときの発火イベント
+ var list = document.getElementById('quakelist');
+ list.onchange = event => {
+ QuakeSelect(list.selectedIndex);
+ }
let maxInt_data = data[0]['earthquake']['maxScale'];
var maxIntText = hantei_maxIntText(maxInt_data);
…
これでリストを選んだときに(list.onchange = event =>
)、QuakeSelect();
という関数を発動させることができます。
QuakeSelect();
という関数はこの下でやります。
また、QuakeSelect(list.selectedIndex);
の「list.selectedIndex」というのは、引数(ひきすう)といい、関数に入力したい値を渡すものです。今回では何番目の選択肢を選んだかを知りたいので、リストのselectedIndex
選ばれた番号を渡しています。
⑤再描画の関数を作成
さて、先ほどでリストの選択肢がクリックされたときに、リストの何番目のnum
を含んだ、QuakeSelect(num)
という関数を発動させる準備ができました。
ではQuakeSelect(num)
を作っていきましょう。
ただし、作ると言っても、前回の④で作成したコードに一部修正を加えるだけです。
let maxInt_data = data[0]['earthquake']['maxScale'];
var maxIntText = hantei_maxIntText(maxInt_data);
var Magnitude = hantei_Magnitude(data[0]['earthquake']['hypocenter']['magnitude']);
var Name = hantei_Name(data[0]['earthquake']['hypocenter']['name']);
var Depth = hantei_Depth(data[0]['earthquake']['hypocenter']['depth']);
var tsunamiText = hantei_tsunamiText(data[0]['earthquake']['domesticTsunami']);
var Time = data[0]['earthquake']['time'];
var shingenLatLng = new L.LatLng(data[0]["earthquake"]["hypocenter"]["latitude"], data[0]["earthquake"]["hypocenter"]["longitude"]);
var shingenIconImage = L.icon({
iconUrl: 'source/shingen.png',
iconSize: [40, 40],
iconAnchor: [20, 20],
popupAnchor: [0, -40]
});
これのどこに修正を加えるかですが、ほとんどのデータを参照するところがdata[0]
で始まっています。
これは、前回では選択できるようにはしていなかったので、最新の地震情報、つまり、JSONの一番初めにくる2地震情報を静的に描画していたことに由来します。
今回では、何番目かを動的に決めるため、[0]
の部分を、今回の引数である[num]
に変えればいいだけです。
よってコードはこう変更となります。
+var shingenIcon;
+function QuakeSelect(num) {
+ if (shingenIcon) {
+ map.removeLayer(shingenIcon);
+ }
- let maxInt_data = data[0]['earthquake']['maxScale'];
+ let maxInt_data = data[num]['earthquake']['maxScale'];
var maxIntText = hantei_maxIntText(maxInt_data);
var Magnitude = hantei_Magnitude(data[num]['earthquake']['hypocenter']['magnitude']);
var Name = hantei_Name(data[num]['earthquake']['hypocenter']['name']);
var Depth = hantei_Depth(data[num]['earthquake']['hypocenter']['depth']);
var tsunamiText = hantei_tsunamiText(data[num]['earthquake']['domesticTsunami']);
var Time = data[num]['earthquake']['time'];
var shingenLatLng = new L.LatLng(data[num]["earthquake"]["hypocenter"]["latitude"], data[num]["earthquake"]["hypocenter"]["longitude"]);
var shingenIconImage = L.icon({
iconUrl: 'source/shingen.png',
iconSize: [40, 40],
iconAnchor: [20, 20],
popupAnchor: [0, -40]
});
- var shingenIcon = L.marker(shingenLatLng, {icon: shingenIconImage }).addTo(map);
+ shingenIcon = L.marker(shingenLatLng, {icon: shingenIconImage }).addTo(map);
shingenIcon.bindPopup('発生時刻:'+Time+'<br>最大震度:'+maxIntText+'<br>震源地:'+Name+'<span style=\"font-size: 85%;\"> ('+data[num]["earthquake"]["hypocenter"]["latitude"]+", "+data[num]["earthquake"]["hypocenter"]["longitude"]+')</span><br>規模:M'+Magnitude+' 深さ:'+Depth+'<br>受信:'+data[num]['issue']['time']+', '+data[num]['issue']['source'],{closeButton: false, zIndexOffset: 10000, maxWidth: 10000});
shingenIcon.on('mouseover', function (e) {this.openPopup();});
shingenIcon.on('mouseout', function (e) {this.closePopup();});
+}
今までの地震情報を描画していたところをQuakeSelect(num)
で囲み、簡単に実行できるようにしたうえで、[0]
を[num]
に変えるだけです。
また、新たな問題として、.addTo(map)
して地図に追加していくだけだと、前の震源アイコンが削除されずに複数のアイコンが地図上に乗ってしまいますので、map.removeLayer(shingenIcon);
でshingenIcon
を削除しています。
これで、リストから選択した地震情報を地図に描画できるようになりました!
終わりに
だいぶ空いてしまったこのシリーズですが、今回はリストから選択した地震情報を描画できるところまでやりました。
次回ではちょっとした改善と情報を更新できるようにしましょう。
今回のサンプル
https://nanka.cloudfree.jp/bin/webapps/shindobunpu_qiita/5/
今回のソースコード
<!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="index.css">
</head>
<body>
<div id="map"></div>
<div class="btns">
<select id="quakelist"><option>地震情報の取得中…</option></select>
</div>
<script src="index.js"></script>
</body>
</html>
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');
var PolygonLayer_Style_nerv = {
"color": "#ffffff",
"weight": 1.5,
"opacity": 1,
"fillColor": "#3a3a3a",
"fillOpacity": 1
}
$.getJSON("prefectures.geojson", function (data) {
L.geoJson(data, {
style: PolygonLayer_Style_nerv
}).addTo(map);
});
$.getJSON("https://api.p2pquake.net/v2/history?codes=551&limit=50", function (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);
}
var shingenIcon;
function QuakeSelect(num) {
if (shingenIcon) {
map.removeLayer(shingenIcon);
}
let maxInt_data = data[num]['earthquake']['maxScale'];
var maxIntText = hantei_maxIntText(maxInt_data);
var Magnitude = hantei_Magnitude(data[num]['earthquake']['hypocenter']['magnitude']);
var Name = hantei_Name(data[num]['earthquake']['hypocenter']['name']);
var Depth = hantei_Depth(data[num]['earthquake']['hypocenter']['depth']);
var tsunamiText = hantei_tsunamiText(data[num]['earthquake']['domesticTsunami']);
var Time = data[num]['earthquake']['time'];
var shingenLatLng = new L.LatLng(data[num]["earthquake"]["hypocenter"]["latitude"], data[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%;\"> ('+data[num]["earthquake"]["hypocenter"]["latitude"]+", "+data[num]["earthquake"]["hypocenter"]["longitude"]+')</span><br>規模:M'+Magnitude+' 深さ:'+Depth+'<br>受信:'+data[num]['issue']['time']+', '+data[num]['issue']['source'],{closeButton: false, zIndexOffset: 10000, maxWidth: 10000});
shingenIcon.on('mouseover', function (e) {this.openPopup();});
shingenIcon.on('mouseout', function (e) {this.closePopup();});
}
});
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;
}
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;
}
#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;
}
次回予告
震度分布図を作ろう⑥ ~情報を更新できるようにしよう~