🌊

指定緊急避難場所ナビゲーションアプリの開発

に公開

はじめに

はじめまして、データアナリティクスラボの石川友都です。今回は災害時の指定緊急避難場所をナビゲートするシステムを考案したので、その紹介をさせていただきます。大がかりなアプリ開発はいずれ行うとして…たたきをStreamlitで開発しました。StreamlitはPythonで簡単なWebアプリを作成することが出来るライブラリです。GISデータの活用事例の一つとしてご参考になれば幸甚です。

本記事はDAL Tech Blog Advent Calendar 2025として投稿しました。全ての記事は以下からご確認いただけます。

https://adventar.org/calendars/12288

指定緊急避難場所とは

さて、指定緊急避難場所とは聞き馴染みのない方もおられるかと存じます。一般的にイメージされる”避難所”が用途により2つに分かれ、その1つがこのように呼ばれます。国土地理院のホームページで分かりやすく解説されています。

指定緊急避難場所

指定緊急避難場所は、災害の危険から命を守るために緊急的に避難する場所 です。
災害発生時は、その災害に対応している指定緊急避難場所へ避難してください。
例えば、大地震が発生し、津波の到達が予想される場合は、津波災害に対応している「指定緊急避難場所」に緊急的に避難します。

指定緊急避難場所
https://www.gsi.go.jp/bousaichiri/hinanbasho.html

指定避難所

指定避難所は、災害の危険がなくなるまでの一時的な避難施設です。
災害発生時には、指定避難所ではなく、指定緊急避難場所へ避難してください。

指定避難所
https://www.gsi.go.jp/bousaichiri/hinanbasho.html

2つを見比べると分かりやすいですが、指定緊急避難場所の方はいわゆる災害発生時にすぐに駆け込むような場所で、指定避難所はその後安全に過ごすための場所だと考えられます。今回作成したシステムの目的は前者にいち早くアクセスするための補助であったので、指定緊急避難場所データを使用しております。上記リンク先より指定緊急避難場所データをダウンロードすることが出来ます。

指定緊急避難場所データ

指定緊急避難場所(以下「避難場所」)の全国版をダウンロードします。私の観測範囲では週に1度ほどのペースで更新されていたので、運用時は定期的なアップデートが必要です。データはcsv形式とgeojson形式の2つが格納されています。

最終更新日が2025年6月18日のデータを開くと以下のような形式です。

NO 共通ID 都道府県名及び市町村名 施設・場所名 住所 洪水 崖崩れ、土石流及び地滑り 高潮 地震 津波 大規模な火事 内水氾濫 火山現象 指定避難所との住所同一 備考 geometry
1 E0110000001202 北海道札幌市 もみじ台中学校 北海道札幌市厚別区もみじ台西1-1-1 1 1 1 1 POINT (141.48471 43.03806)
2 E0110000002202 北海道札幌市 もみじの森小学校 北海道札幌市厚別区もみじ台西3-4-1 1 1 1 1 POINT (141.48629 43.03174)
3 E0110000003201 北海道札幌市 熊の沢公園 北海道札幌市厚別区もみじ台西6 1 POINT (141.49061 43.0292)
4 E0110000005202 北海道札幌市 もみじの丘小学校 北海道札幌市厚別区もみじ台東4-5-1 1 1 1 1 POINT (141.49765 43.02982)
5 E0110000010202 北海道札幌市 厚別西小学校 北海道札幌市厚別区厚別西三条1-3-1 1 1 1 POINT (141.45775 43.04816)

同ページ内にカラムの定義があります。

データ項目の名称 定義
NO 1 から連番
共通ID 内閣府が定めたルールに従って付番 全国共通避難所・避難場所ID(共通IDについて)
施設・場所名 指定緊急避難場所の名称
住所 指定緊急避難場所の住所、「都道府県/市町村/丁目/番地」の順
洪水 対象とする異常な現象の種類が「洪水」の指定緊急避難場所、該当は「1」、非該当は無記入
崖崩れ、土石流及び地滑り 対象とする異常な現象の種類が「崖崩れ、土石流及び地滑り」の指定緊急避難場所、該当は「1」 、非該当は無記入
高潮 対象とする異常な現象の種類が「高潮」の指定緊急避難場所、該当は「1」 、非該当は無記入
地震 対象とする異常な現象の種類が「地震」の指定緊急避難場所、該当は「1」 、非該当は無記入
津波 対象とする異常な現象の種類が「津波」の指定緊急避難場所、該当は「1」 、非該当は無記入
大規模な火事 対象とする異常な現象の種類が「大規模な火事」の指定緊急避難場所、該当は「1」 、非該当は 無記入
内水氾濫 対象とする異常な現象の種類が「内水氾濫」の指定緊急避難場所、該当は「1」 、非該当は無記入
火山現象 対象とする異常な現象の種類が「火山現象」の指定緊急避難場所、該当は「1」 、非該当は無記入
指定避難所との住所同一 指定避難所と住所が一致する指定緊急避難場所、該当は「1」 、非該当は無記入
緯度 緯度の値、 10 進法
経度 経度の値、 10 進法
備考 補足事項がある場合に記載

上記のテーブルはgeojson形式のファイルをgeopandasで読み込んだものですので、定義テーブルの緯度と経度がgeometryとまとめられているようです。各災害種類ごとに対象避難場所であれば1が降られています。

全体構想

小規模なものであってもアプリの開発をしたことがないので、よく見るフローチャート図を作るのは恐らく初めてでした。下図のような流れを実装しました。

全体構成図

位置情報の取得

位置情報の取得には2つのパターンを用意しました。1つ目は現在地の取得です。やはりGoogle Mapのように現在位置情報を取得できるのがUX的にも最適です。2つ目に検索を用意しました。現在地以外の場所、例えば職場や子供の学校、旅行先などの避難場所を事前に知っておくというユースケースを想定しました。

現在地の取得

現在地の取得にはmisogihagi様が作成されたstreamlit_current_locationを利用させていただきました。リンクはこちらです。streamlitを用いるファイル上で動作し、Web APIの位置情報を取得してくれます。

import streamlit as st
from streamlit_current_location import current_position

position = current_position()
st.text(f'緯度: {position['latitude']}')
st.text(f'経度: {position['longitude']}')

上記のコードでstreamlit runを実行すると、画面上に緯度と経度が表示されます。

検索地の取得

Pythonで位置情報を検索することが出来るライブラリはいくつかありますが、今回はGeopyのNominatim(名称検索)を活用しました。日本国内に絞っても名称検索では重複が存在するため、実際にパブリックに運用する場合は別途考える必要があります。

import streamlit as st
from shapely import Point
from geopy.geocoders import Nominatim

geolocator = Nominatim(user_agent = 'test')
spot = st.text_input(
    label = '検索したい場所を入力してください。',
    placeholder = '例: 東京タワー',
    label_visibility = 'visible'
)

if spot:
    searched_location = geolocator.geocode(query = spot, country_codes = 'jp')
    st.text(f'緯度: {searched_location.latitude}')
    st.text(f'経度: {searched_location.longitude}')

周囲に該当災害に対応する避難所があるかの確認

先ほど紹介した避難場所は当たり前ですが日本全国に点在しています。ですので一旦目的地点の周囲に避難場所があるかを確認します。閾値として初めに直線距離で3,000m以内の避難所をピックアップします。もし半径3,000m以内に該当災害に対する避難場所が存在しない場合には、順次1,000mずつ範囲を拡大していきます。初期値を3,000mとしたのは大体歩いて1時間以内が現実的かと考えたためです。

ここで直線距離の算出には、座標系を平面直角座標系に変換してからメートル単位の距離を算出することを最初に考えましたが、目的地点がどこかによって採用する座標系が変わってしまいます。日本全国で19に分かれておりますが、これが都道府県単位ではなく、地点によって座標系が異なる場合があるため平面直角座標系の採用は難しいと感じました。例えば北海道は東部・中部・西部の3つに分かれていますし、離島は属する都道府県と異なる座標系に設定されているケースが多々あります。詳しくは国土地理院のこちらのページを参照してください。

なので今回はgeopyのdistanceメソッドから測地距離(geodesic)を採用しました。geopyのdistanceメソッドにはデフォルトの測地距離のほかに大円距離(great circle)のオプションも用意されています。大円距離は球体において2地点と中心を通る平面上の2地点間の弧の長さと言えますが、地球は完全な球体ではないため誤差が生じます。一方で測地距離は完全な球体ではなく、地球が楕円体であるときの形状を考慮にいれたうえでの最短距離です。詳細は割愛しますが、すべての大円距離は測地距離となりますが、測地距離は必ずしも大円距離とは限りません。いずれにせよ国内の3,000m程度の範囲では誤差は限られますが、今回はデフォルトの測地距離を採用しています。なお、国土地理院の指定緊急避難場所データの経緯度も測地系がWGS84であるため、geopy.distaceでもデフォルトとなっているWGS84のまま距離を算出します。

実装時には計算コストを考えて、目的地点から3,000mの地点に実際には円ではなく72角形となるshapely.Polygonオブジェクトを作成し、そのポリゴン内に存在する避難場所を抽出する形を取りました。繰り返しになりますが、最初に3,000mの72角形に避難場所が1つもなければさらに1,000mを足して見つかるまで繰り返します。

最短距離の指定緊急避難場所を特定

さて、例えば最初のアプローチである3,000m以内の範囲に対象災害に対応する避難場所が複数存在したとします。ここでもgeopy.distanceを用いて目的地点から各避難場所までの直線距離を計算して最短距離にある避難場所を特定します。

import geopandas as gpd
from shapely import Point, Polygon

def create_circle(location: Point, radius: int) -> Polygon:
    angles = range(0, 360, 360 // 72)
    points = []

    for angle in angles:
        point = distance(meters = radius).destination((location.y, location.x), bearing = angle)
        points.append((point.longitude, point.latitude))
    
    circle = Polygon(points)

    return circle

# 指定緊急避難場所データから、選択した災害(type_disaster列)フラグがTrueの避難場所を抽出
gdf_shlters = gpd.read_file('/xxx/shelters.geojson')
gdf_disaster = gdf_shlters[gdf_shlters[type_disaster] == '1']

radius = 3000
length = 0

# 避難場所の数が1つも存在しなければ範囲を1,000m広げて再度確認
while length < 1:
    circle = create_circle(location, radius)

    # 指定半径の円内の避難場所を抽出
    gdf_neighborhood = gdf_disaster[circle.contains(gdf_disaster['geometry'])]
    length = len(gdf_neighborhood)
    radius += 1000

foliumで地図を描画

foliumは簡単に地図を描画することが出来るライブラリです。streamlit上でも活用することが出来ますが、streamlit上ではstreamlit_foliumライブラリを使用します。ここではfolium.Mapオブジェクトを作成したうえで、そこに目的地点を中心として近隣の避難場所をプロットします。目的地点は先に選択した「現在地」もしくは「検索地」になります。近隣の避難場所は目的地点から3,000m以内(存在しない場合は1,000mずつ順次拡大)に存在する避難場所すべてです。例えば、弊社が入居している汐留イーストサイドビルで「洪水」を対象として検索をすると以下のような表示がされます。赤いピンが目的地点である汐留イーストサイドビル、水色ピンが洪水に対応する避難場所です。

デフォルトのズーム値を16と設定しているので3,000mの範囲を網羅してはいませんが、マップをインタラクティブに拡大/縮小することが出来ます(上図はスクリーンショットなので拡大/縮小できません)。この画面から、洪水に対応している近隣の避難場所は2つあることが分かります。

徒歩の道のりを検索

最後に最寄りの避難場所までの徒歩の経路を地図に描画して完成です。徒歩の経路検索にはopen route serviceを活用しました。open route serviceでは今回の経路検索のほかにも様々な地理情報データ活用を可能にするAPIが揃えられています。

目的地点から最寄りの避難場所を探してそこまでの経路を地図にプロットしたいのですが、正確には徒歩経路をすべて算出してから最寄りを判断すべきです。ただし今回はAPIの使用回数を極力減らすために、直線距離で最寄りとなる避難場所までの経路のみ算出してプロットすることとしました。open route serviceの通常版では、2,000回/日、40回/分の回数制限があります(2025年7月時点)。

取得した経路を地図に重ね合わせると以下のようになります。先と検索条件は同様です。

無事に徒歩経路を地図上に重ね合わせることが出来ていることが分かります。

課題

今回は災害発生時にどこにいても近くの指定緊急避難場所をすぐに確認できるようなアプリを開発することを目的としましたが、いくつか課題がありました。

題目上避けられないテーマですが、一般にリリースするとなると情報の誤りや如何なるエラーは許されず、場合によっては命を落とされる方が出てしまう可能性が拭いきれません。考えられるのは、取得した現在地点のズレ、検索地点の誤り(同名の別地点となる可能性)、緊急避難場所データの更新遅れ、表示された経路の可用性(場所によっては洪水時に川に架かる橋を渡る経路も)など枚挙に暇がありません。またサービスの性質上、災害発生時には平常時とは比較にならないアクセスが予想されるため、大量のアクセスに耐えうる強さが必要となります。むしろこれが確認できなければリリースは出来ません。

したがって、今回は上記の課題点を踏まえて弊社内で利用しているコミュニケーションツールのSlackで社員がいつでも指定緊急避難場所を確認できるような形に着地させました。

社内での活用事例

  1. 任意の場所でスラッシュコマンドを実行してアプリを呼び出す

  2. ポップアップが現れるので、確認したい災害の種類と場所を入力 (現在地には対応していない)

  3. gis-evacuation-navi-botからメッセージが届く

おわりに

ここまでお読みいただきありがとうございます!Streamlitによる簡単なアプリ開発でしたが、データサイエンティストとしてこのように一連の動作を構築するのはよい機会となりました。GISデータを用いた社会課題解決は今後大きな潮流となることが予想されています。解決例、または課題の提示例の一つとして、この記事が皆様のお役に立てましたら幸いです。

DAL Tech Blog

Discussion