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

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

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

![](https://www.esrij.com/cgi-bin/wp/wp-content/uploads/2015/05/Platform_p7_2.png)
引用元: [ユーザーのデータを活かす、豊富なオンラインマップコンテンツ](https://www.esrij.com/products/arcgis/features/data/)

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

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

* [D3.js + jsdomでSVGファイルを作成](href="https://cieloazul310.github.io/2017/12/d3-to-svgfile-with-jsdom/)
* [Node.js + d3 + jsdomを使用し、サーバー側でsvgを生成](https://symfoware.blog.fc2.com/blog-entry-2115.html)

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

<img src="https://qiita-image-store.s3.amazonaws.com/0/135313/22962596-41a5-75c0-963a-cd7b7623f5eb.png" width="360px" alt="jma-20190114042730395-14132353.png"/>
引用元: [気象庁 地震情報](https://www.jma.go.jp/jp/quake/quake_local_index.html)

<img src="https://qiita-image-store.s3.amazonaws.com/0/135313/db5cbfd3-169f-29f1-0df6-572bf55a2316.jpeg" width="360px" alt="nhk-JS00clA0190114132353_20190114132731.jpg"/>
引用元: [NHK 地震情報](https://www.nhk.or.jp/kishou-saigai/earthquake/)

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


# 構成
![thumbnail](https://qiita-image-store.s3.amazonaws.com/0/135313/8fea63f7-1315-5912-0637-c4075d032698.jpeg)

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

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型](http://xml.kishou.go.jp/xmlpull.html)
2. [EqCare Type-G(バージョン1)](https://doc01.pf.iij-engineering.co.jp/pub/sdkdoc/v1/ja_JP/restapi/restapi_pub_jmaearthquake_result.html) (正確には気象庁のデータをベースにIIJが提供しているようです)

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

### 気象庁防災情報XMLフォーマット形式電文 PULL型
> 更新サイクルの異なる２つのAtomフィードが取得可能であり、自動処理等、用途に合わせてご利用ください。
> Atomフィードの分類種別
>　・定時：気象に関する情報のうち、天気概況など定時に発表されるもの
>　・随時：気象に関する情報のうち、警報・注意報など随時発表されるもの
>　・地震火山：地震、火山に関する情報
>　・その他：上記３種類のいずれにも属さないもの
>
> <http://xml.kishou.go.jp/xmlpull.html>

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


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

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

こんな感じです。

```xml
<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">【震源・震度情報】１７日２３時１０分ころ、地震がありました。</content>
  </entry>
  ...
```

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

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

```xml
<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>１７日２３時１０分ころ、地震がありました。</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="北緯３４．９度　東経１３７．９度　深さ　１０ｋｍ" datum="日本測地系">+34.9+137.9-10000/</jmx_eb:Coordinate>
        </Area>
      </Hypocenter>
      <jmx_eb:Magnitude type="Mj" description="Ｍ３．１">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画像が出力できました。


# 結果
<img src="https://qiita-image-store.s3.amazonaws.com/0/135313/af7ece8d-0951-1ef8-c202-afa94b872fdb.png" width="400px" alt="20190114132300_茨城県南部_local.png"/>


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

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

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

```js
// 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)](https://doc01.pf.iij-engineering.co.jp/pub/sdkdoc/v1/ja_JP/restapi/restapi_pub_jmaearthquake_result.html)を利用するのが扱いやすいと思います。


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

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

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

以下に地震情報XML内の市町村コードの情報がまとめられていますが、地点コードと地点名の対応しかとれません。そのため、地点名から更に緯度経度を変換する必要があります。
> [気象庁防災情報XMLフォーマット | 技術資料](http://xml.kishou.go.jp/tec_material.html)
> 上記サイトのコード管理表及び個別コード表の『個別コード表[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>