1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

気象庁サイトを利用したビューアの作成 16 警報+氾濫警報(地図)

1
Posted at

目次・全体的な注意点(第1回の記事)

前回は一般の警報と氾濫警報(指定河川洪水予報)を表形式で表示してきましたが、今回は地図形式で表示してみます

以前の記事と重複する内容は簡単に取り上げるにとどめています

それぞれご確認ください

apiのURL

他の情報と同様、ブラウザの開発者ツールでネットワークタブを開いた状態で気象庁ホームページにアクセスし、Fetch/XHRでフィルタするとデータを格納したファイルが見つかります

地図表示に必要なデータは、

2次細分区域ごとのポリゴンデータは全国を10個に分割して格納されており、URL中の{i}は0~9の数字です。
各数字と対応する緯度経度の範囲は https://www.jma.go.jp/bosai/common/const/relm.json に格納されています

apiの構造

気象庁ホームページの内部apiの構造は、気象庁防災情報XMLの構造をモデルとしているものが多いです。このため、気象庁防災情報XMLの解説資料が参考になる場合が多いです

一般の警報・氾濫警報の電文の説明は「気象警報・注意報(R06)_解説資料.pdf」「指定河川洪水予報(氾濫警報・注意報)_解説資料.pdf」としてそれぞれまとめられており、この資料の内容が参考になります

一般の警報の発表状況(map.json)

map.json(抜粋)
[
  {
    "reportDatetime": "2026-06-25T15:53:00+09:00",
    "publishingOffice": "稚内地方気象台",
    "warning": {
      "class10Items": [
        {
          "areaCode": "011000",
          "kinds": [
            { "code": "15", "status": "発表"}
          ]
        },...
      ],
      "class20Items": [
        {
          "areaCode": "0121400",
          "kinds": [
            { "code": "15", "status": "発表"}
          ]
        },...
      ]
    }
  },...
]

{map.json}.{i}.warning.class10Items 以下に1次細分区域ごとの警報の発表状況が格納されています。class20Items以下には2次細分区域ごとの情報が入っており、どちらも形式は同じです

class10Items.{j}.areaCode が1次細分区域コード、class10Items.{j}.kinds.codeが警報コード、class10Items.{j}.kinds.statusが発表状態です。

codeは警報・注意報の種別を表すもので、気象庁防災情報の警報の解説資料の別表に掲載があります(かっこ付きの情報は2026年時点では運用されていません)

現象 特別警報 危険警報 警報 注意報
大雨 33 43 03 10
土砂災害 39 49 09 29
氾濫 34 44 04 18
高潮 38 48 08 19
大雪 36 (46) 06 12
暴風/強風 35 (45) 05 15
暴風雪/風雪 32 (42) 02 13
波浪 37 (47) 07 16
14
融雪 17
濃霧 20
乾燥 21
なだれ 22
低温 23
24
着氷 25
着雪 26
その他 (27)

statusは「発表」「継続」「警報から注意報」「危険警報から注意報」etc. の 発表されていることを表すものと、「解除」「発表警報・注意報はなし」の 発表されていないことを表すものに大別されます。
statusが「解除」「発表警報・注意報はなし」以外の場合に発表されているとみなし、codeを読みに行けばOKです

flood_xml.json
[
  {
    "publishingOffice": "東京都 気象庁",
    "editorialOffice": "気象庁本庁",
    "reportDatetime": "2026-06-03T14:50:00+09:00",
    "infoType": "発表",
    "serial": "2",
    "item": {
      "areas": [
        { "name": "目黒川", "code": "130005000100"}
      ],
      "name": "レベル2氾濫注意報解除",
      "code": "10",
      "condition": "レベル2氾濫注意報解除"
    },
    "pdfFilename": "130005000100_20260602205000_n00.pdf",
    "riverCode": "130005000100",
    "riverName": "目黒川",
    "class20Codes": [ "1310900", "1311000"],
    "class10Codes": [ "130010"],
    "officeCodes": [ "130000"]
  },......
]

氾濫警報については、{i}.item.code に警報種別のコード番号が、{i}.class10Codes に警戒が必要な地域の1次細分区域コードが、{i}.class20Codes に同じく2次細分区域コードが格納されています。
一般の警報と同じく1次細分区域ごと/2次細分区域ごとの危険度を表現する場合には、1つの自治体が複数の川の流域に含まれる場合もあることから、一度全部の氾濫警報電文を読んで最も危険度が高いものをとる必要があります

警報のコード番号は気象庁防災情報XMLの氾濫警報の解説資料に掲載がありますが、以下の通りです。幸いにも十の位が危険度と一致しているので、コード番号の上1桁を警戒レベルと解釈する雑な処理でOKです

name code
レベル2氾濫注意報解除 10
レベル2氾濫注意報 20
レベル2氾濫注意報 21
レベル2氾濫注意報(警報解除) 22
レベル3氾濫警報 30
レベル3氾濫警報 31
レベル4氾濫危険警報 40
レベル4氾濫危険警報 41
レベル5氾濫特別警報 51
レベル5氾濫特別警報(氾濫水の予報) 53

class10s.json、class20s_{i}.jsonはgeoJson形式となっています(中身の説明は割愛)

class20s_0.json~class20s_9.jsonがそれぞれどの地域を表すかは、relm.jsonに格納されています

relm.json
[
  {"ne":[45.5569,148.8922],"sw":[41.3521,139.3344]}, // 0 北海道
  {"ne":[41.5559,142.0725],"sw":[38.7478,139.6932]}, // 1 北東北
  {"ne":[39.2086,141.6747],"sw":[36.7365,137.635]}, // 2 南東北・新潟
  {"ne":[37.1543,153.9864],"sw":[24.2254,138.3971]}, // 3 関東
  {"ne":[37.8553,139.1766],"sw":[34.5781,136.2439]}, // 4 北陸東海甲信
  {"ne":[36.2953,136.9877],"sw":[33.433,134.2527]}, // 5 近畿福井三重
  {"ne":[37.2429,134.8208],"sw":[32.7025,131.668]}, // 6 中国四国
  {"ne":[34.7987,132.4913],"sw":[31.9887,128.3437]}, // 7 山口・九州北部
  {"ne":[33.1944,131.8857],"sw":[27.0187,128.3955]}, // 8 九州南部
  {"ne":[27.8853,131.3312],"sw":[24.0456,122.9337]} // 9 沖縄
]

0番目が class20s_0.json の担当範囲、1番目がclass20s_1.json の担当範囲… です

表示用のコード

ここまでの内容を使って、地図表示用のコードを書いていきます

アメダスのデータを地図表示した記事の時などと同様に、Leaflet.jsを用いて地図表示していきます

headタグ内でLeafletのスタイルシートとスクリプトを読み込みます(Leafletのチュートリアルからそのまま拝借)

<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>

ページのレイアウトを定義します。html、bodyを画面幅いっぱいに表示する設定とし、flexでメニュー部分(div#menu)と地図部分(div#map)に分割します

<style>
  *{ font-family:sans-serif;}
  html, body{ width:100%; height:100%; margin:0; overflow:hidden;}
  body{ display:flex; flex-direction:column;}
  div#menu{ flex:0.01;}
  div#map{ flex:0.99; background:#3b4580;}
</style>

警報コード別の種類、レベルと、レベルごとの色を定義します。種類は、例えば「大雪」を選択したときに大雪特別警報・大雪警報・大雪注意報をまとめて表示するために使います

const warnInfos = {
  "00":{"long":"解除","kind":"none","lv":0},
  "32":{"long":"暴風雪特別警報","kind":"wind_snow","lv":5},
  "33":{"long":"レベル5大雨特別警報","kind":"rain","lv":5},
//  ......
  "29":{"long":"レベル2土砂災害注意報","kind":"landslide","lv":2}
}
const warnColors = { "5":"#0c000c", "4":"#aa00aa", "3":"#ff2800", "2":"#f2e700", "0":"#c8c8cb"};

1次細分区域ごとの地図と、2次細分区域ごとの地図を表示するレイヤー(layers)を定義します。また、警報・氾濫警報の発表状況をまとめるオブジェクト(warnFloods)を定義します

let globals = {"class20Polygons":{}}, layers = { 'class10s':L.layerGroup(), 'class20s':{}}, warnFloods = {"class10s":{},"class20s":{}};
for( let i=0; i<=9; i++){
  layers['class20s'][i] = L.layerGroup();
}

地図のレイヤーを定義します。発表中の警報に応じた色分けの層と、自治体の境界線を表示する層の2層を定義します

// <- 表側 pane2:境界線 pane1:警報(塗り) 裏側->
for( let i=0; i<3; i++){
  map.createPane("pane"+i).style.zIndex = i*10;
}

警報電文を順番に読んでいき、warnFloods[地域種別][地域コード][警報種類]=警報レベル のような形でまとめていきます

warnFloods = {"class10s":{},"class20s":{}};
for( let warn of datas['warns']){
  for( let areaType of ['class10','class20']){
    for( let area of warn['warning'][areaType+'Items']){
      let areaCode = area['areaCode'];
      if( warnFloods[areaType+'s'][areaCode]==undefined){
        warnFloods[areaType+'s'][areaCode] = {};
      }
      for( let kind of area['kinds']){
        if( kind['status']=="解除" || kind['status']=="発表警報・注意報はなし"){
          continue;
        }
        const warnCode = kind['code'], warnKind = warnInfos[warnCode]['kind'], warnLv = warnInfos[warnCode]['lv'];
        if( warnFloods[areaType+'s'][areaCode][warnKind]==undefined || warnFloods[areaType+'s'][areaCode][warnKind]<warnLv){
          warnFloods[areaType+'s'][areaCode][warnKind] = warnLv;
        }
        if( warnFloods[areaType+'s'][areaCode]['all']==undefined || warnFloods[areaType+'s'][areaCode]['all']<warnLv){
          warnFloods[areaType+'s'][areaCode]['all'] = warnLv;
        }
      }
    }
  }
}

氾濫警報電文も順番に読んでいき、警報と同じように warnFloods[地域種別][地域コード][警報種類(='flood')]=警報レベル の形にまとめていきます。こちらは、1つの市町村に対して複数の川の氾濫警報が発表されていることがあるため、既に値がある場合は高い方をとるようにします

for( let flood of datas['floods']){
  let floodCode = flood['item']['code'], floodLv = floodCode[0]*1;
  if( floodLv==1){
    floodLv = 0;
  }
  for( let areaType of ['class10','class20']){
    for( let areaCode of flood[areaType+'Codes']){
      if( warnFloods[areaType+'s'][areaCode]==undefined){
        warnFloods[areaType+'s'][areaCode] = {};
      }
      if( warnFloods[areaType+'s'][areaCode]['flood']==undefined || warnFloods[areaType+'s'][areaCode]['flood']<floodLv){
        warnFloods[areaType+'s'][areaCode]['flood'] = floodLv;
      }
      if( warnFloods[areaType+'s'][areaCode]['all']==undefined || warnFloods[areaType+'s'][areaCode]['all']<floodLv){
        warnFloods[areaType+'s'][areaCode]['all'] = floodLv;
      }
    }
  }
}

地図を動かした際には地図の領域を読み取り、まだ取得していない2次細分区域のポリゴンデータ(class20s_{i}.json)がある場合には取得します

map.on('moveend', function () {
  getClass20s();
});

function getClass20s(){
  let promises = [];
  for( let i=0; i<=9; i++){
    promises.push(
      new Promise((resolve, reject)=>{
        let ne = L.latLng(globals['areaLimits'][i]['ne']), sw = L.latLng(globals['areaLimits'][i]['sw']);
        let polygonBounds = L.latLngBounds(ne,sw);
        if( 8<=map.getZoom() && polygonBounds.intersects(map.getBounds())){
          if( globals['class20Polygons'][i]==undefined){
            fetch("https://www.jma.go.jp/bosai/common/const/geojson/class20s_" + i + ".json")
            .then((response) => response.json())
            .then((response) => {
              globals['class20Polygons'][i] = response;
              resolve(i);
            })
          }else{
            resolve(i);
          }
        }else{
          resolve(i);
        };
      })
    );
  }
  Promise.allSettled(promises).then((values)=>{
    drawWarns();
  });
}

1次細分区域・2次細分区域ごとの警報の発表状況を描画していきます

function drawWarns(){
  const targetWarnKind = document.getElementById('targetWarnKind').value;

  L.geoJSON( globals['class10Polygons'], {
    style: function(feature) {
      let warnLevel = 0, weight = 1, areaCode = feature['properties']['code'];
      if( warnFloods['class10s'][areaCode]!=undefined && warnFloods['class10s'][areaCode][targetWarnKind]!=undefined){
        warnLevel = warnFloods['class10s'][areaCode][targetWarnKind];
      }
      if( feature['properties']['islandBold']){
        weight = 8;
      }
      return { fillColor:warnColors[warnLevel], fillOpacity:1, weight:weight, color:warnColors[warnLevel], pane:"pane1"};
    }
  }).addTo(layers['class10s']);
  for( let i in globals['class20Polygons']){
    L.geoJSON( globals['class20Polygons'][i], {
      style: function(feature) {
        let warnLevel = 0, weight = 1, areaCode = feature['properties']['code'];
        if( warnFloods['class20s'][areaCode]!=undefined && warnFloods['class20s'][areaCode][targetWarnKind]!=undefined){
          warnLevel = warnFloods['class20s'][areaCode][targetWarnKind];
        }
        if( feature['properties']['islandBold']){
          weight = 8;
        }
        return { fillColor:warnColors[warnLevel], fillOpacity:1, weight:weight, color:warnColors[warnLevel], pane:"pane1"};
      }
    }).addTo(layers['class20s'][i]);
  }
  controlLayers();
}

拡大倍率が大きい場合には1次細分区域ごとのレイヤーを、小さい場合には2次細分区域ごとのレイヤーを表示します

function controlLayers(){
  if( 8<=map.getZoom()){
    if( map.hasLayer(layers['class10s'])){
      map.removeLayer( layers['class10s']);
    }
    for( let i in globals['class20Polygons']){
      if( !map.hasLayer(layers['class20s'][i])){
        map.addLayer( layers['class20s'][i]);
      }
    }
  }else{
    if( !map.hasLayer(layers['class10s'])){
      map.addLayer( layers['class10s']);
    }
    for( let i in globals['class20Polygons']){
      if( map.hasLayer(layers['class20s'][i])){
        map.removeLayer( layers['class20s'][i]);
      }
    }
  }
}

表示ページ全体のソースコード

サンプルページ

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>警報(地図)</title>
  <!-- https://leafletjs.com/ -->
  <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>
  <style>
    *{ font-family:sans-serif;}
    html, body{ width:100%; height:100%; margin:0; overflow:hidden;}
    body{ display:flex; flex-direction:column;}
    div#menu{ flex:0.01;}
    div#map{ flex:0.99; background:#3b4580;}
  </style>
</head>
<body>
  <div id="menu">
    <select id="targetWarnKind">
      <option value="all">全て</option>
      <option value="rain">大雨</option>
      <option value="landslide">土砂災害</option>
      <option value="flood">氾濫</option>
      <option value="tide">高潮</option>
      <option value="wind">暴風・強風</option>
      <option value="wind_snow">暴風雪・風雪</option>
      <option value="snow">大雪</option>
      <option value="wave">波浪</option>
      <option value="thunder"></option>
      <option value="snow_melting">融雪</option>
      <option value="fog">濃霧</option>
      <option value="dry">乾燥</option>
      <option value="avalanche">なだれ</option>
      <option value="cold">低温</option>
      <option value="frost"></option>
      <option value="ice_accretion">着氷</option>
      <option value="snow_accretion">着雪</option>
    </select>
  </div>
  <div id="map"></div>
  <script>
    "use strict";
    const params = new URLSearchParams(window.location.search);
    const warnInfos = {
      "00":{"long":"解除","kind":"none","lv":0},
      "32":{"long":"暴風雪特別警報","kind":"wind_snow","lv":5},
      "33":{"long":"レベル5大雨特別警報","kind":"rain","lv":5},
      "34":{"long":"レベル5氾濫特別警報","kind":"flood","lv":5},
      "35":{"long":"暴風特別警報","kind":"wind","lv":5},
      "36":{"long":"大雪特別警報","kind":"snow","lv":5},
      "37":{"long":"波浪特別警報","kind":"wave","lv":5},
      "38":{"long":"レベル5高潮特別警報","kind":"tide","lv":5},
      "39":{"long":"レベル5土砂災害特別警報","kind":"landslide","lv":5},
      "42":{"long":"暴風雪危険警報","kind":"wind_snow","lv":4},
      "43":{"long":"レベル4大雨危険警報","kind":"rain","lv":4},
      "44":{"long":"レベル4氾濫危険警報","kind":"flood","lv":4},
      "45":{"long":"暴風危険警報","kind":"暴風","lv":4},
      "46":{"long":"大雪危険警報","kind":"snow","lv":4},
      "47":{"long":"波浪危険警報","kind":"wave","lv":4},
      "48":{"long":"レベル4高潮危険警報","kind":"tide","lv":4},
      "49":{"long":"レベル4土砂災害危険警報","kind":"landslide","lv":4},
      "02":{"long":"暴風雪警報","kind":"wind_snow","lv":3},
      "03":{"long":"レベル3大雨警報","kind":"rain","lv":3},
      "04":{"long":"レベル3氾濫警報","kind":"flood","lv":3},
      "05":{"long":"暴風警報","kind":"wind","lv":3},
      "06":{"long":"大雪警報","kind":"snow","lv":3},
      "07":{"long":"波浪警報","kind":"wave","lv":3},
      "08":{"long":"レベル3高潮警報","kind":"tide","lv":3},
      "09":{"long":"レベル3土砂災害警報","kind":"landslide","lv":3},
      "10":{"long":"レベル2大雨注意報","kind":"rain","lv":2},
      "12":{"long":"大雪注意報","kind":"snow","lv":2},
      "13":{"long":"風雪注意報","kind":"wind_snow","lv":2},
      "14":{"long":"雷注意報","kind":"thunder","lv":2},
      "15":{"long":"強風注意報","kind":"wind","lv":2},
      "16":{"long":"波浪注意報","kind":"wave","lv":2},
      "17":{"long":"融雪注意報","kind":"snow_melting","lv":2},
      "18":{"long":"レベル2氾濫注意報","kind":"flood","lv":2},
      "19":{"long":"レベル2高潮注意報","kind":"tide","lv":2},
      "20":{"long":"濃霧注意報","kind":"fog","lv":2},
      "21":{"long":"乾燥注意報","kind":"dry","lv":2},
      "22":{"long":"なだれ注意報","kind":"avalanche","lv":2},
      "23":{"long":"低温注意報","kind":"cold","lv":2},
      "24":{"long":"霜注意報","kind":"frost","lv":2},
      "25":{"long":"着氷注意報","kind":"ice_accretion","lv":2},
      "26":{"long":"着雪注意報","kind":"snow_accretion","lv":2},
      "27":{"long":"その他の注意報","kind":"other","lv":2},
      "29":{"long":"レベル2土砂災害注意報","kind":"landslide","lv":2}
    }
    const warnColors = { "5":"#0c000c", "4":"#aa00aa", "3":"#ff2800", "2":"#f2e700", "0":"#c8c8cb"};
    let globals = {"class20Polygons":{}}, layers = { 'class10s':L.layerGroup(), 'class20s':{}}, warnFloods = {"class10s":{},"class20s":{}};
    for( let i=0; i<=9; i++){
      layers['class20s'][i] = L.layerGroup();
    }
    let map = L.map('map').setView([35, 135], 5);
    // <- 表側 pane2:境界線 pane1:警報(塗り) 裏側->
    for( let i=0; i<3; i++){
      map.createPane("pane"+i).style.zIndex = i*10;
    }

    getGlobals();

    function getGlobals(){
      let promises = [];
      let urls = {
        "areaLimits":"https://www.jma.go.jp/bosai/common/const/relm.json",
        "class10Polygons":"https://www.jma.go.jp/bosai/common/const/geojson/class10s.json",
      }
      for( let elemName in urls){
        promises.push(
          new Promise((resolve, reject) => {
            fetch(urls[elemName])
            .then((response) => response.json())
            .then((response) => {
              globals[elemName] = response;
              resolve( elemName);
            });
          })
        )
      }
      Promise.allSettled(promises).then((values) => {
        initialize();
      });
    }

    function initialize(){
      L.control.layers({
        "気象庁地図":L.tileLayer('https://www.jma.go.jp/tile/jma/base/{z}/{x}/{y}.png', { minZoom:4, maxZoom:12, maxNativeZoom:10, attribution: '<a href="https://www.jma.go.jp/jma/kishou/info/coment.html">気象庁</a>', pane:"pane2"}).addTo(map),
      }).addTo(map);
      get();
    }

    function get(){
      let promises = [], datas = {};
      let urls = {
        "warns":"https://www.jma.go.jp/bosai/warning/data/r8/map.json",
        "floods":"https://www.jma.go.jp/bosai/flood/data/r8/flood_xml.json"
      }
      for( let elemName in urls){
        promises.push(
          new Promise((resolve, reject) => {
            fetch(urls[elemName])
            .then((response) =>  response.json())
            .then((response) => {
              datas[elemName] = response;
              resolve(elemName);
            });
          })
        );
      }
      Promise.allSettled(promises).then((values) => {
        jointWarnFloods( datas);
      });
    }

    function jointWarnFloods( datas){
      warnFloods = {"class10s":{},"class20s":{}};
      for( let warn of datas['warns']){
        for( let areaType of ['class10','class20']){
          for( let area of warn['warning'][areaType+'Items']){
            let areaCode = area['areaCode'];
            if( warnFloods[areaType+'s'][areaCode]==undefined){
              warnFloods[areaType+'s'][areaCode] = {};
            }
            for( let kind of area['kinds']){
              if( kind['status']=="解除" || kind['status']=="発表警報・注意報はなし"){
                continue;
              }
              const warnCode = kind['code'], warnKind = warnInfos[warnCode]['kind'], warnLv = warnInfos[warnCode]['lv'];
              if( warnFloods[areaType+'s'][areaCode][warnKind]==undefined || warnFloods[areaType+'s'][areaCode][warnKind]<warnLv){
                warnFloods[areaType+'s'][areaCode][warnKind] = warnLv;
              }
              if( warnFloods[areaType+'s'][areaCode]['all']==undefined || warnFloods[areaType+'s'][areaCode]['all']<warnLv){
                warnFloods[areaType+'s'][areaCode]['all'] = warnLv;
              }
            }
          }
        }
      }
      for( let flood of datas['floods']){
        let floodCode = flood['item']['code'], floodLv = floodCode[0]*1;
        if( floodLv==1){
          floodLv = 0;
        }
        for( let areaType of ['class10','class20']){
          for( let areaCode of flood[areaType+'Codes']){
            if( warnFloods[areaType+'s'][areaCode]==undefined){
              warnFloods[areaType+'s'][areaCode] = {};
            }
            if( warnFloods[areaType+'s'][areaCode]['flood']==undefined || warnFloods[areaType+'s'][areaCode]['flood']<floodLv){
              warnFloods[areaType+'s'][areaCode]['flood'] = floodLv;
            }
            if( warnFloods[areaType+'s'][areaCode]['all']==undefined || warnFloods[areaType+'s'][areaCode]['all']<floodLv){
              warnFloods[areaType+'s'][areaCode]['all'] = floodLv;
            }
          }
        }
      }
      console.log( warnFloods);
      removeLayers();
    }

    document.getElementById("targetWarnKind").addEventListener("change", function(e){
      removeLayers();
    });

    function removeLayers(){
      if( map.hasLayer(layers['class10s'])){
        map.removeLayer(layers['class10s']);
      }
      for( let i=0; i<=9; i++){
        if( map.hasLayer(layers['class20s'][i])){
          map.removeLayer(layers['class20s'][i]);
        }
      }

      layers['class10s'] = L.layerGroup();
      for( let i=0; i<=9; i++){
        layers['class20s'][i] = L.layerGroup();
      }
      getClass20s();
    }

    map.on('moveend', function () {
      getClass20s();
    });

    function getClass20s(){
      let promises = [];
      for( let i=0; i<=9; i++){
        promises.push(
          new Promise((resolve, reject)=>{
            let ne = L.latLng(globals['areaLimits'][i]['ne']), sw = L.latLng(globals['areaLimits'][i]['sw']);
            let polygonBounds = L.latLngBounds(ne,sw);
            if( 8<=map.getZoom() && polygonBounds.intersects(map.getBounds())){
              if( globals['class20Polygons'][i]==undefined){
                fetch("https://www.jma.go.jp/bosai/common/const/geojson/class20s_" + i + ".json")
                .then((response) => response.json())
                .then((response) => {
                  globals['class20Polygons'][i] = response;
                  resolve(i);
                })
              }else{
                resolve(i);
              }
            }else{
              resolve(i);
            };
          })
        );
      }
      Promise.allSettled(promises).then((values)=>{
        drawWarns();
      });
    }

    function drawWarns(){
      const targetWarnKind = document.getElementById('targetWarnKind').value;

      L.geoJSON( globals['class10Polygons'], {
        style: function(feature) {
          let warnLevel = 0, weight = 1, areaCode = feature['properties']['code'];
          if( warnFloods['class10s'][areaCode]!=undefined && warnFloods['class10s'][areaCode][targetWarnKind]!=undefined){
            warnLevel = warnFloods['class10s'][areaCode][targetWarnKind];
          }
          if( feature['properties']['islandBold']){
            weight = 8;
          }
          return { fillColor:warnColors[warnLevel], fillOpacity:1, weight:weight, color:warnColors[warnLevel], pane:"pane1"};
        }
      }).addTo(layers['class10s']);
      for( let i in globals['class20Polygons']){
        L.geoJSON( globals['class20Polygons'][i], {
          style: function(feature) {
            let warnLevel = 0, weight = 1, areaCode = feature['properties']['code'];
            if( warnFloods['class20s'][areaCode]!=undefined && warnFloods['class20s'][areaCode][targetWarnKind]!=undefined){
              warnLevel = warnFloods['class20s'][areaCode][targetWarnKind];
            }
            if( feature['properties']['islandBold']){
              weight = 8;
            }
            return { fillColor:warnColors[warnLevel], fillOpacity:1, weight:weight, color:warnColors[warnLevel], pane:"pane1"};
          }
        }).addTo(layers['class20s'][i]);
      }
      controlLayers();
    }
    
    function controlLayers(){
      if( 8<=map.getZoom()){
        if( map.hasLayer(layers['class10s'])){
          map.removeLayer( layers['class10s']);
        }
        for( let i in globals['class20Polygons']){
          if( !map.hasLayer(layers['class20s'][i])){
            map.addLayer( layers['class20s'][i]);
          }
        }
      }else{
        if( !map.hasLayer(layers['class10s'])){
          map.addLayer( layers['class10s']);
        }
        for( let i in globals['class20Polygons']){
          if( map.hasLayer(layers['class20s'][i])){
            map.removeLayer( layers['class20s'][i]);
          }
        }
      }
    }
</script>
</body>
</html>
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?