GISコミュニティフォーラム開催報告:ArcGISユーザーのためのデータハンドリング ~さまざまなデータ活用の第一歩~

Document created by tomoyasu_fukuiesrij-esridist Employee on Jun 13, 2019Last modified by tomoyasu_fukuiesrij-esridist Employee on Jun 20, 2019
Version 5Show Document
  • View in full screen mode

はじめに

2019年 5 22 () 24 ()、東京ミッドタウン 六本木にて第15GIS コミュニティフォーラム及びプレフォーラム・セミナーが開催されました!

公開可能なセッションの発表資料は ESRIジャパンのサポートサイトで公開しております。また、GeoNetブログでは開発者向けのセッションで使用したコードなどを順次ご紹介していきます。発表資料や GeoNet記事、GitHub へのリンクなどの詳細は第 15 回 GIS コミュニティフォーラム開催報告記事をご覧ください。

 

この記事は「ArcGIS ユーザーのためのデータハンドリング ~さまざまなデータ活用の第一歩~」セッションのスピンオフです。

 

概要

本編ではいくつかのデータを例に Python を活用したデータハンドリングの手法をご紹介しました。今回のブログでは、その中の、「ひと手間必要なデータ④」でご紹介した、気象庁が提供している XML電文 (震源・震度に関する情報) 予報区の GIS データをハンドリングしてマップに可視化する手法に、本編ではご紹介しきれなかった、 Web スクレイピング処理を加えた、一連の処理をご紹介します。

 

今回は、Python を使って気象庁が提供している XML 電文を Web スクレイピング処理によって抽出、成型し、「予報区のGISデータ」から作成するポリゴン フィーチャと結合します。そして地震発生時点のフィーチャ クラスとして保存します。

次の流れでコードを解説します。

  1. 気象庁のサイトより XML の情報を取得
  2. XML から必要な情報の抽出とポリゴン フィーチャの作成
  3. 抽出した情報とポリゴン フィーチャを結合

 

コードと解説

Tips:スクレイピング実施時の注意について

Web ページ上の情報には著作権や利用規約が存在します。

また、過度なアクセスは Web サーバーに負荷をかけてしまうことになります。

スクレイピングを行う際は、あらかじめサービス側の規約で許可されているか確認の上ご対応お願いいたします。

1)気象庁のサイトより XML の情報を取得

まずは処理を実行するために必要なパッケージのインポートと変数を定義します。

本編で説明した、XML の操作を行うために必要な「xml.etree.ElementTree」と GIS データを操作するために必要な「arcpy」に加え、本編では使用しなかった「urllib.request」をインポートします。Urllib.request URL への接続、ファイルの読込みに使用します。   

import xml.etree.ElementTree as ET
import urllib.request
import arcpy

WORK_SPACE = r"D:\data\DemoProject\DemoProject.gdb"
SHAPE_FILE = r"D:\data\DemoProject\20190125_AreaForecastLocalE_GIS\地震情報/細分区域.shp"

 

そして、「urllib.request」の機能を使用して 指定した URL (http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml) から XML データを取得し、「xml.etree.ElementTree」の機能を使用して 取得した情報から title タグが「震源・震度に関する情報」となっている情報のみを再抽出しています。

def load_xml():
    """
    気象庁のサイトよりXMLの情報を取得する
    """

    url = 'http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml'

    xml_string = download(url)
    elem = ET.fromstring(xml_string)
    target_dict = {}
    atom_ns = 'http://www.w3.org/2005/Atom'

    for e in elem.findall('.//{%s}entry' % atom_ns):
        s_title = e.find('./{%s}title' % atom_ns).text
        s_updated = e.find('./{%s}updated' % atom_ns).text
        s_url = e.find('./{%s}link' % atom_ns).attrib
        s_content = e.find('./{%s}content' % atom_ns).text

        if s_title=='震源・震度に関する情報':
            target_dict[s_content] = [s_url, s_updated]

    # コンテンツごとのURLを作成
    for key in target_dict.keys():
        d = target_dict[key]
        _url = d[0]['href']
        _xml_string = download(_url)
        #XML読込
        read_xml(_xml_string)

def download(url):
    """
    URL で指定したXMLを取得する
    """

    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as response:
        xml_string = response.read()

    return xml_string

TipsXML の名前空間について

コード上で指定している「atom_ns = 'http://www.w3.org/2005/Atom'」はXML の名前空間です。XML はタグを独自に作成することが魅力の一つですが、同一名のタグを作成することもできてしまうため、重複することもあります。そのため、XML には"名前空間"という仕組みがあり、同一のタグ名でも名前空間が異なれば異なる要素として取り扱うことができます。

 

2)XML から必要な情報の抽出とポリゴン フィーチャの作成

XML ファイルから「地震発生時刻」と「震度」、「エリアコード」の情報を抽出しています。そして、「arcpy」の機能を使い、ジオプロセシングツールを使用して予報区の GIS データ (地震情報/細分区域.shp) から抽出した「地震発生時刻」の値を名称にポリゴン フィーチャを作成しています。

def read_xml(_xml_):
    """
    XMLをパースします。Area要素内のCodeがフィーチャクラスに存在する場合、
    MaxIntを使用してフィーチャクラスの震度カラムを更新します。
    """

    elem = ET.fromstring(_xml_)

    for elem in elem.iter():
        if elem.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}OriginTime":
            s_originTime = elem.text #地震発生時刻を取得
            #フィーチャクラスの名前作成
            outfc = arcpy.ValidateTableName("地震情報_" + s_originTime)
            #フィーチャクラスの作成
            create_flg = create_feature(outfc)
            if create_flg == False:
                break
               
        if elem.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Pref":
            for el in list(elem):
                if el.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Area":
                    for e in list(el):
                        code = el.find("{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Code") #コード取得
                        maxInt = el.find("{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}MaxInt") #震度取得
                        update(maxInt.text, code.text, outfc) #フィーチャクラスの更新

def create_feature(outfc):
    #ワークスペースにすでに同一名のフィーチャクラスがないかチェック
    if arcpy.Exists(outfc):
        return False
    #Shapeファイルをフィーチャクラスに変換
    arcpy.CopyFeatures_management(SHAPE_FILE, outfc)
    #フィーチャクラスにカラム追加
    arcpy.AddField_management(outfc, "震度", "Text")
    #座標系変換
    arcpy.DefineProjection_management(outfc, arcpy.SpatialReference(4612))
    return True

 

3)抽出した情報とポリゴン フィーチャを結合

処理2)で抽出した「震度」と「エリアコード」の情報を「arcpy」のカーソル機能を使用して、ポリゴン フィーチャがもつエリアコードと紐づけを行い、該当するフィーチャに対して震度の情報を更新しています。

def update(maxInt, code, outfc):
    """
    フィーチャクラスを更新します
    """

    with arcpy.da.UpdateCursor(outfc,"震度", "code = %s" % "'" + code + "'") as cursor:
        for row in cursor:
            row[0] = maxInt #震度カラムを更新
            cursor.updateRow(row)

 

全体コード

これまで説明したコードを一つにまとめると以下のようなコードになります。

変数に指定しているデータをご自身の環境に合わせて変更していただければそのまま使用できます。

import xml.etree.ElementTree as ET
import urllib.request
import arcpy

WORK_SPACE = r"D:\data\DemoProject\DemoProject.gdb"
SHAPE_FILE = r"D:\data\DemoProject\20190125_AreaForecastLocalE_GIS\地震情報/細分区域.shp"

def read_xml(_xml_):
    """
    XMLをパースします。Area要素内のCodeがフィーチャクラスに存在する場合、
    MaxIntを使用してフィーチャクラスの震度カラムを更新します。
    """

    elem = ET.fromstring(_xml_)

    for elem in elem.iter():
        if elem.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}OriginTime":
            s_originTime = elem.text #地震発生時刻を取得
            #フィーチャクラスの名前作成
            outfc = arcpy.ValidateTableName("地震情報_" + s_originTime)
            #フィーチャクラスの作成
            create_flg = create_feature(outfc)
            if create_flg == False:
                break
               
        if elem.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Pref":
            for el in list(elem):
                if el.tag == "{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Area":
                    for e in list(el):
                        code = el.find("{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}Code") #コード取得
                        maxInt = el.find("{http://xml.kishou.go.jp/jmaxml1/body/seismology1/}MaxInt") #震度取得
                        update(maxInt.text, code.text, outfc) #フィーチャクラスの更新

def create_feature(outfc):
    #ワークスペースにすでに同一名のフィーチャクラスがないかチェック
    if arcpy.Exists(outfc):
        return False
    #Shapeファイルをフィーチャクラスに変換
    arcpy.CopyFeatures_management(SHAPE_FILE, outfc)
    #フィーチャクラスにカラム追加
    arcpy.AddField_management(outfc, "震度", "Text")
    #座標系変換
    arcpy.DefineProjection_management(outfc, arcpy.SpatialReference(4612))
    return True

def update(maxInt, code, outfc):
    """
    フィーチャクラスを更新します
    """

    with arcpy.da.UpdateCursor(outfc, "震度", "code = %s" % "'" + code + "'") as cursor:
        for row in cursor:
            row[0] = maxInt #震度カラムを更新
            cursor.updateRow(row)

def load_xml():
    """
    気象庁のサイトよりXMLの情報を取得する
    """

    url = 'http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml'

    xml_string = download(url)
    elem = ET.fromstring(xml_string)
    target_dict = {}
    atom_ns = 'http://www.w3.org/2005/Atom'

    for e in elem.findall('.//{%s}entry' % atom_ns):
        s_title = e.find('./{%s}title' % atom_ns).text
        s_updated = e.find('./{%s}updated' % atom_ns).text
        s_url = e.find('./{%s}link' % atom_ns).attrib
        s_content = e.find('./{%s}content' % atom_ns).text

        if s_title=='震源・震度に関する情報':
            target_dict[s_content] = [s_url, s_updated]

    # コンテンツごとのURLを作成
    for key in target_dict.keys():
        d = target_dict[key]
        _url = d[0]['href']
        _xml_string = download(_url)
        #XML読込
        read_xml(_xml_string)

def download(url):
    """
    URL で指定したXMLを取得する
    """

    req = urllib.request.Request(url)
    with urllib.request.urlopen(req) as response:
        xml_string = response.read()

    return xml_string

if __name__ == '__main__':
    #ワークスペース指定
    arcpy.env.workspace = WORK_SPACE
    #XMLダウンロード
    load_xml()

 

コードを実行すると以下のような属性データを持つフィーチャ クラスのデータが作成されます

 

さいごに

今回のブログでは本編で語れなかった、スクレイピングの処理を追加したデータハンドリングをご紹介しました。

本記事でご紹介したコードは ESRIジャパンの GitHub でも公開しています。また、ハンドリングを行ったデータを使って Web GIS のデータに対する更新処理、タスクスケジューラーを使った定期更新の処理など、できることはたくさんあります。関連情報にも参考となる情報を記載いたしますので、ArcGIS を活用した、今後のデータハンドリングにぜひお役立てください。

 

関連情報

Attachments

    Outcomes