Help us understand the problem. What is going on with this article?

気象庁防災情報XMLとD3.jsを使って地震の震度分布図を作る

はじめに

ちょっとした要望から地震の震度分布図を作ることになりました。
この記事では構成の解説や感想をまとめています。

震度分布図を作るといってもやり方はいろいろあります。例えばArcGISのようなGIS系ソフトを使えば、手軽にグラデーションで表現することも可能です。


引用元: ユーザーのデータを活かす、豊富なオンラインマップコンテンツ

ただし今回は画像生成機能をシステムに組み込む予定だったため 、GUIソフトは利用しませんでした。

また仕様(要望)として「高品質かつ後の編集用にベクター形式が望ましい」とのことだったので、d3.jsを利用してSVG画像を生成することに。d3の場合、jsdomと組み合わせればブラウザレスでプログラム的にSVGを生成することができます。

上画像のようなグラデーションで表現するとなると実装が大変かもしれませんが、気象庁やNHKが公開しているような震度を点で表現する画像であればSVGでも比較的手軽に生成できます。

jma-20190114042730395-14132353.png
引用元: 気象庁 地震情報

nhk-JS00clA0190114132353_20190114132731.jpg
引用元: NHK 地震情報

今回作る画像は上2つを参考にしました。

構成

thumbnail

大まかな流れはこんな感じです。

  1. 地図データ(GeoJson)の用意
  2. 地震データの取得
  3. 地震データ内の地点コードを緯度経度に変換
  4. d3-geoで緯度経度を画像系座標に変換
  5. jsdom上でSVG要素を生成&出力

キーワード

  • 気象庁防災情報XMLフォーマット形式電文
  • 震度観測地点位置JSON
  • GeoJson
  • D3.js
  • d3-geo
  • jsdom

解説

ここではやり方について解説します。

地図データ(GeoJson)の用意

今回は以下の日本地図データを利用しました。
https://github.com/dataofjapan/land

ただし、そのままではデータの密度が高く、無駄に画像容量が大きくなってしまいます。表示する地図のズームレベルに応じてデータを間引いたほうが、容量を削減できます。
https://github.com/maxogden/simplify-geojson

ちなみに容量削減のためにtopojsonを利用する手段もありますが、緯度経度情報が消えてしまうため震度地点(緯度経度)を重ねて描画できなくなってしまいます。そのため今回はTopoJsonは利用しません。

地震情報の取得

地震情報を提供するサービスと言えば気象庁のものが有名どころかと思います。

  1. 気象庁防災情報XMLフォーマット形式電文 PULL型
  2. EqCare Type-G(バージョン1) (正確には気象庁のデータをベースにIIJが提供しているようです)

2のサービスはJSON形式に対応しているため良さげだったのですが、今回は大元の1. 気象庁防災情報XMLフォーマット形式電文 PULL型を使うことにしました。

気象庁防災情報XMLフォーマット形式電文 PULL型

更新サイクルの異なる2つのAtomフィードが取得可能であり、自動処理等、用途に合わせてご利用ください。
Atomフィードの分類種別
 ・定時:気象に関する情報のうち、天気概況など定時に発表されるもの
 ・随時:気象に関する情報のうち、警報・注意報など随時発表されるもの
 ・地震火山:地震、火山に関する情報
 ・その他:上記3種類のいずれにも属さないもの

http://xml.kishou.go.jp/xmlpull.html

取得できる内容の詳細は以下にまとめられています。
http://xml.kishou.go.jp/open_trial/xmllist.pdf

地震火山フィードでは以下3種類の地震情報が公開されています。正確には各種地震エントリー内に、震度情報XMLのURLが含まれています。

  • 震源・震度に関する情報
  • 震源に関する情報
  • 震度速報

こんな感じです。

<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
  <title>高頻度(地震火山)</title>
  <subtitle>JMAXML publishing feed</subtitle>
  <updated>2019-01-17T23:14:01+09:00</updated>
  <id>urn:uuid:71738065-cd47-3c04-b4a5-e46f6ee5e89c</id>
  <link rel="related" href="http://www.jma.go.jp/"/>
  <link rel="self" href="http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml"/>
  <link rel="hub" href="http://alert-hub.appspot.com/"/>

  <rights type="html">
    <![CDATA[
    <a href="http://www.jma.go.jp/jma/kishou/info/coment.html">利用規約</a>, <a href="http://www.jma.go.jp/jma/en/copyright.html">Terms of Use</a>
    ]]>
  </rights>

  <entry>
    <title>震源・震度に関する情報</title>
    <id>urn:uuid:8ac1b426-28ca-3989-8c7c-5e895c48a174</id>
    <updated>2019-01-17T14:13:36Z</updated>
    <author>
    <name>気象庁</name>
    </author>
    <link type="application/xml" href="http://www.data.jma.go.jp/developer/xml/data/8ac1b426-28ca-3989-8c7c-5e895c48a174.xml"/>
    <content type="text">【震源・震度情報】17日23時10分ころ、地震がありました。</content>
  </entry>
  ...

その中で、各地震度計の情報を含んでいる「震源・震度に関する情報」を今回は利用しました。

震源・震度に関する情報の中身

<Report xmlns="http://xml.kishou.go.jp/jmaxml1/" xmlns:jmx="http://xml.kishou.go.jp/jmaxml1/">
  <Control>
    <Title>震源・震度に関する情報</Title>
    <DateTime>2019-01-17T14:13:36Z</DateTime>
    <Status>通常</Status>
    <EditorialOffice>気象庁本庁</EditorialOffice>
    <PublishingOffice>気象庁</PublishingOffice>
  </Control>

  <Head xmlns="http://xml.kishou.go.jp/jmaxml1/informationBasis1/">
    <Title>震源・震度情報</Title>
    <ReportDateTime>2019-01-17T23:13:00+09:00</ReportDateTime>
    <TargetDateTime>2019-01-17T23:13:00+09:00</TargetDateTime>
    <EventID>20190117231049</EventID>
    <InfoType>発表</InfoType>
    <Serial>1</Serial>
    <InfoKind>地震情報</InfoKind>
    <InfoKindVersion>1.0_1</InfoKindVersion>
    <Headline>
      <Text>17日23時10分ころ、地震がありました。</Text>
    </Headline>
  </Head>

  <Body xmlns="http://xml.kishou.go.jp/jmaxml1/body/seismology1/" xmlns:jmx_eb="http://xml.kishou.go.jp/jmaxml1/elementBasis1/">
    <Earthquake>
      <OriginTime>2019-01-17T23:10:00+09:00</OriginTime>
      <ArrivalTime>2019-01-17T23:10:00+09:00</ArrivalTime>
      <Hypocenter>
        <Area>
        <Name>静岡県西部</Name>
        <Code type="震央地名">443</Code>
        <jmx_eb:Coordinate description="北緯34.9度 東経137.9度 深さ 10km" datum="日本測地系">+34.9+137.9-10000/</jmx_eb:Coordinate>
        </Area>
      </Hypocenter>
      <jmx_eb:Magnitude type="Mj" description="M3.1">3.1</jmx_eb:Magnitude>
    </Earthquake>

    <Intensity>
      <Observation>
        <CodeDefine>
          <Type xpath="Pref/Code">地震情報/都道府県等</Type>
          <Type xpath="Pref/Area/Code">地震情報/細分区域</Type>
          <Type xpath="Pref/Area/City/Code">気象・地震・火山情報/市町村等</Type>
          <Type xpath="Pref/Area/City/IntensityStation/Code">震度観測点</Type>
        </CodeDefine>
        <MaxInt>2</MaxInt>
        <Pref>
          <Name>静岡県</Name>
          <Code>22</Code>
          <MaxInt>2</MaxInt>
          <Area>
            <Name>静岡県西部</Name>
            <Code>443</Code>
            <MaxInt>2</MaxInt>
            <City>
              <Name>掛川市</Name>
              <Code>2221300</Code>
              <MaxInt>2</MaxInt>
              <IntensityStation>
                <Name>掛川市長谷*</Name>
                <Code>2221340</Code>
                <Int>2</Int>
              </IntensityStation>
            </City>
            ...

地点コードを緯度経度へ変換

気象庁の地震情報XMLの中には、震度地点のコードは含まれているものの緯度経度はありません。地図上に描画するためには地点コードを位置情報に変換する必要があります。

震度観測地点位置JSONでは各地点コードと緯度経度がまとめられいますので、これを変換に利用しました。
https://data.svir.jp/intens-st/api/v3/

SVG画像の生成

d3-geoを利用して緯度経度情報を画像系座標に変換し、SVGを生成する流れは以下の記事が参考になりました。
https://qiita.com/sand/items/422d4fab77ea8f69dfdf

記事の流れに従ってSVG画像が出力できました。

結果

20190114132300_茨城県南部_local.png

感想

気象庁の配信する地震データ形式がXMLで扱いづらい

今回は気象庁が直に公開している地震情報を利用したかったため、気象庁防災情報XMLフォーマット形式電文を利用しましたが、前述の通りこれはXML形式にしか対応していません。

プログラムから利用する場合、XMLをパースする必要があります。またパースして連想配列(Object)形式に変換しても、データの取り出しがいまいちわかりずらいです。

以下はXMLのパース結果から日付や震源地などを取得する例です。

// get eq info
  const areaName = result.Report.Body[0].Earthquake[0].Hypocenter[0].Area[0].Name[0];
  const date = cleanDateStr(result.Report.Body[0].Earthquake[0].OriginTime[0]);

  // get epicenter
  const epicenterInfo = result.Report.Body[0].Earthquake[0].Hypocenter[0].Area[0]["jmx_eb:Coordinate"][0]._;
  const epicenter = getEpicenter(epicenterInfo);

  // max intensity
  const maxIntensity = parseInt(result.Report.Body[0].Intensity[0].Observation[0].MaxInt[0]);
  // get area intensity
  const intensityInfo = result.Report.Body[0].Intensity[0].Observation[0].Pref;

データの提供元に制約がないのであれば、JSONでの出力に対応しているEqCare Type-G(バージョン1)を利用するのが扱いやすいと思います。

地点コードから緯度経度への変換が面倒

地震情報のレスポンス内に地点の緯度経度情報が含まれていれば楽なのですが、そうでないため自分で変換テーブルを用意する必要があります。

また、今回は行っていませんが、市町村コードを変換する場合は少しやっかいです。地震情報XML内の市町村コード(Cityタグ)は7桁フォーマットのものが採用されていて、総務省が定めている一般的な5・6桁の地点コード(全国地方公共団体コード)とは互換性がないようです。

以下に地震情報XML内の市町村コードの情報がまとめられていますが、地点コードと地点名の対応しかとれません。そのため、地点名から更に緯度経度を変換する必要があります。

気象庁防災情報XMLフォーマット | 技術資料
上記サイトのコード管理表及び個別コード表の『個別コード表[zip形式]』内の、「地震火山関連コード表.xls」の震度観測点コード表シート(24)、「震度観測点」列の定義を参照。
http://xml.kishou.go.jp/tec_material.html

画像生成に関する機能はすべてNode.jsで完結

外部サービスを除き、主要機能部分はすべてNodeで完結しているため、Package.jsonで動作環境を管理することができています。可動性、移植性、開発コストなど様々な面おいて、一つの言語で動くのはありがたい限りです。仕事でもNode.jsを扱うことは増えてきましたが、今後もっとできることが増えていくといいですね。

追記

今回の記事内容を元に、地震発生後、自動的に震度分布画像を生成するシステムを作ってみました。
https://wp.blog.icchi.me/earthquake-map-system

icchi_h
都内のメディア企業で働く高専出身エンジニア
https://icchi.me
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away