GIS奮闘記

元GISエンジニアの技術紹介ブログ。主にPythonを使用。

スポンサーリンク

Python で CSV ファイルを SQLite にインポートする方法

さて、本日は CSV ファイルを SQLite にインポートしてみようと思います。前回のエントリーで「交通事故統計情報のオープンデータ」のコード表を CSV に変換してみました。本エントリーではその CSV を SQLite にインポートしてみようと思います。

PDF を CSV に変換する方法について興味のある方はぜひ前回のエントリーを読んでみてください。

www.gis-py.com

また、以下のエントリーで SQLite に関して書いていますので、こちらも興味がありましたらぜひ読んでみてください。

www.gis-py.com

対象データ

前回のエントリーで出力した CSV です。 これらをファイルごとにインポートしていこうと思います。

f:id:sanvarie:20201229171900p:plain

f:id:sanvarie:20201229172351p:plain

やり方

  1. Pandas で データフレームとして CSV を読み込む
  2. 「1」のデータフレームをSQLiteにインポート

実行環境

Windows10 64bit
Python3.6.6

サンプルコード

CSV ファイルを SQLite にインポートするサンプルです。すごく短いコードで済みましたね。

# -*- coding: utf-8 -*-
import sqlite3
from os.path import join, split
from glob import glob
import pandas as pd

csv_path = r"D:\blog\data\pdf2csv\csv"
db_path = r"D:\blog\data\sqlite\test.db"

def insert_csv():
    # CSV ファイルのディレクトリ取得
    csv_files = glob(join(csv_path, "*.csv"))

    for csv in csv_files:

        table_name = split(csv)[1] # テーブル名作成

        table_name = table_name[0:-4] # テーブル名から拡張子を削除

        df = pd.read_csv(csv, dtype=object) # CSV 読込

        with sqlite3.connect(db_path) as conn:
            df.to_sql(table_name, con=conn) # SQLiteにCSVをインポート

if __name__ == '__main__':
    insert_csv()

結果を確認するとこのように CSV がSQLiteにインポートされたことがわかります。

f:id:sanvarie:20201229185546p:plain

f:id:sanvarie:20201229183843p:plain

さいごに

CSV ファイルの SQLite へのインポートは思ったよりも簡単にできることがわかりました(Pandas は本当に便利なライブラリですね)。これでようやく「交通事故統計情報のオープンデータ」の可視化にとりかかれそうです。ちょっとまだすべてのデータを確認したわけではないので何とも言えないのですが、結構大変そうですね。ただ、今年の年末年始はコロナでたっぷり時間がありそうですし楽しみながら取り掛かってみようと思います。次回のエントリーもぜひ読んでみてください。

Python で PDF を CSV に変換してみよう!

さて、本日は Python で PDF を CSV に変換してみようと思います。実は「交通事故統計情報のオープンデータ」を可視化してみようと思ったのですが、そこで使用しようと思ったデータの一部がPDFで公開されており(データ自体はCSVだったのですが、コード表がPDFで公開されていました)、まず最初にPDF → CSV 変換について書いてみようと思いました。

変換対象

「交通事故統計情報のオープンデータ」のコード表ですね。できれば別のフォーマットで公開していただければ手間が省けたのですが・・・

以下のように色々なパターンのページがあることがわかりました。

①コード表説明とコード表

f:id:sanvarie:20201229173050p:plain

②コード表のみ(コード表が複数ページにまたがっている)

f:id:sanvarie:20201229170736p:plain

③コード表説明とコード表 とコード表の補足(画面ショット右端の表)

f:id:sanvarie:20201229170810p:plain

④コード表説明のみ

f:id:sanvarie:20201229170928p:plain

これらの中から以下をCSV に変換してみようと思います。
- ①のコード表(コード表説明は省く)
- ②のコード表
- ③のコード表(コード表説明は省く)

使用するライブラリ

tabula-py というライブラリを使用します(Python 3.5+必須)。このライブラリを使用することで PDFの表を pandas の データフレームに変換することができます。

※本ライブラリを使用するには Java 8+ が必要なので、こちらもインストールします。

実行環境

Windows10 64bit
Python3.6.6

サンプルコード

「交通事故統計情報のオープンデータ」の各コード表を CSV に変換するサンプルです。

# -*- coding: utf-8 -*-
import os
import pandas as pd
import tabula

file_in = r"D:\blog\data\pdf2csv\codebook_2019.pdf"
file_out = r"D:\blog\data\pdf2csv\csv"

def check_columns(df, previous_df):
    """前ページと現ページのデータフレーム比較"""
    difference1 = set(df.keys()) - set(previous_df.keys())
    difference2 = set(previous_df.keys()) - set(df.keys())

    if (len(difference1) == 0 and len(difference2) == 0):
        return True
    else:
        return False

def pds2csv():
    """PDF → CSV 変換処理"""
    df_list = tabula.read_pdf(file_in, lattice=True, pages = 'all')

    concat_flg = 0
    previous_df = ""
    master_name = ""
    for df in df_list:

        # 各コード区分の概要説明の箇所は抽出対象外とする
        if (df.columns[0] == "項目名"):

            # 数ページにまたがっているコード表の出力
            if (concat_flg == 1):
                previous_df.to_csv(os.path.join(file_out, master_name + ".csv"), index=False)
                concat_flg = 0

            # 出力ファイル名作成
            master_name = df.columns[1]
            master_name = df[master_name][0].replace('、', '_') + "_" + master_name

        # 25ページ目の一部表は不要
        elif (df.columns[0] == "コード(下1桁)"):
            pass
        # 数ページにまたがっているコード表の結合
        elif (check_columns(df, previous_df)):
            df = pd.concat([previous_df, df])
            concat_flg = 1
        # CSV 出力
        else:
            df.to_csv(os.path.join(file_out, master_name + ".csv"), index=False)
            concat_flg = 0

        previous_df = df

if __name__ == '__main__':
    pds2csv()

結果を確認するとこのようにCSV が作成されたことがわかりました。

f:id:sanvarie:20201229171900p:plain

中身も問題なさそうですね。全部のファイルは調べていないのですが、とりあえず変換はできたっぽいです(もし何か不備がありましたら本エントリーにコメントをいただけると助かります)。

f:id:sanvarie:20201229172351p:plain

さいごに

PDF に記載されている内容を CSV に変換したいというケースはけっこうあると思います(特にオープンデータまわりを触ることが多い方)。そんな時に tabula-py はとても便利な存在かと思います。興味のある方はぜひ使ってみてください。PDF から CSV の変換が終わったのでさっそく「交通事故統計情報のオープンデータ」の可視化にとりかかりたいところですが、その前に今回変換した CSV を DB に格納するなど何かしらの処理が必要な気がしています。なので次回は その辺について書いてみようと思います。

Amazon Polly と Python で音声合成をしてみよう!

さて、本日は Amazon Polly と Python を使って音声合成(テキストなどによって入力した言葉を読み上げさせること)をしてみようと思います。ここ数年どんどん存在感を増してきている AWS ですが、本当に色々なサービスがあり便利ですね。

Amazon Polly とは?

Amazon Polly はテキストをリアルなスピーチに変換するクラウドサービスです。毎月 500万文字まで無料らしいので、非常に良心的ですね。

必要なライブラリ

Python で AWS まわりの操作をするには、boto3 というライブラリが必要です。pip install boto3 でインストールしてください。

読み上げるテキスト

コードにべた書きですが、今回は「GIS奮闘記」という文章を音声合成してみようと思います。

実行環境

Windows10 64bit
Python3.6.6

サンプルコード

# -*- coding: utf-8 -*-
import os
from tempfile import gettempdir
from contextlib import closing
from boto3 import client

polly = client("polly",
               region_name="us-east-1",
               aws_access_key_id="****",
               aws_secret_access_key="****")

response = polly.synthesize_speech(Text = "GIS奮闘記",
                                   OutputFormat = "mp3",
                                   VoiceId = "Mizuki")

if "AudioStream" in response:
    with closing(response["AudioStream"]) as stream:
        output = os.path.join(gettempdir(), "speech.mp3")

        with open(output, "wb") as file:
            file.write(stream.read())

os.startfile(output)

作成された音声ファイルが以下になります。

https://drive.google.com/file/d/1ZgowBcCQPDJw9U1C0lLZ9ZVfdQ1zk6H_/view?usp=sharing

ばっちり「GIS奮闘記」ち読み上げられていますね!

さいごに

この技術を使用すれば Web サイトや スマホアプリの中で色々なテキストの読み上げをすることができますね。こんなサービスを用意してくれている AWS は本当にありがたい存在です。Python からの使用もとても簡単なので興味のある方はぜひ Amazon Polly を使ってみてください。本日は以上です。

Python と Googleマップのタイムライン機能を使って自分の移動履歴を可視化してみよう!

さて、本日は Googleマップのタイムライン機能を使って自分の移動履歴を可視化してみようと思います。自分の移動履歴が見れるなんて面白そうですね!

タイムライン機能とは?

Googleマップの機能で自分がいる「場所」と「時間」を記録してくれるものです。

どのように記録したデータを利用するのか?

ここから JSON としてタイムライン機能が記録したデータをダウンロードすることができます。ただし、タイムライン機能をオンにしておく必要があるので、使用してみたいという方は先に設定を確認した方がいいかと思います。

appllio.com

どのように可視化するか

以下のエントリーと同じく PyShp と ArcGIS Pro を使用します。

www.gis-py.com

手順

  1. タイムライン機能が記録したデータをダウンロード(KML と JSON をダウンロードできますが、今回は JSON をダウンロードします)
  2. PyShp を使ってJSON を Shape ファイルに変換
  3. ArcGIS Proで可視化

1. JSON をダウンロード

1.https://takeout.google.com/settings/takeout/custom/location_history に行き、「次のステップ」を押します。

f:id:sanvarie:20201010074328p:plain

2.「エクスポートを作成」を押します。

f:id:sanvarie:20201010074407p:plain

3.「ダウンロード」を押します。

f:id:sanvarie:20201010074542p:plain

4.ロケーション履歴.json がダウンロードされます(以下のイメージでは緯度経度をマスキングしています) f:id:sanvarie:20201011212111p:plain

2. PyShp を使って json を Shape ファイルに変換

json をダウンロードしたら次はそれを Shape ファイルに変換します。

実行環境

Windows10 64bit
Python3.6.6
ArcGIS Pro2.6

サンプルコード

少し冗長になってしまいましたが、ロケーション履歴.json を Shape ファイルに変換するサンプルコードです。ポイントとラインの Shape ファイルに変換しています。

# -*- coding: utf-8 -*-
import json
from datetime import datetime
import shapefile

input_file = r"D:\python\data\GoogleMaps\Takeout\ロケーション履歴\ロケーション履歴.json"
output_file_point = r'D:\python\data\googlemaps\timeline_point.shp'
output_file_line = r'D:\python\data\googlemaps\timeline_line.shp'
output_projection_file_point = r'D:\python\data\googlemaps\timeline_point'
output_projection_file_line = r'D:\python\data\googlemaps\timeline_line'

def convert_time(timestamp):
    """ unix 時間を変換"""

    t = datetime.fromtimestamp(float(timestamp)/1000)
    return (t.year, t.month, t.day, t.hour, t.minute, t.second)

def create_projection_file(file):
    """ プロジェクションファイル作成"""

    with open("%s.prj" % file, "w") as prj:
        epsg = 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]'
        prj.write(epsg)

def create_line():
    """ライン作成"""

    with shapefile.Writer(output_file_line) as w:

        # とりあえず属性は以下の一つとする
        w.field("年月日", "C")

        # json 読込
        json_open = open(input_file, 'r')
        json_load = json.load(json_open)

        verticles = []
        date = ""
        previous_date = ""

        for j in json_load["locations"]:

            timestamp = convert_time(j["timestampMs"])
            lat = float(j["latitudeE7"])/10000000
            lon = float(j["longitudeE7"])/10000000

            # 年月日ごとにラインを作成
            date = str(timestamp[0]) + "/" + str(timestamp[1]) + "/" + str(timestamp[2])

            if (len(verticles) == 0):
                verticles.append([lon, lat])
            elif (previous_date != "" and date != previous_date):

                if(len(verticles) > 1):
                    w.line([verticles])
                    w.record(previous_date)
                    # いったんクリア
                    verticles = []
                    verticles.append([lon, lat])
                elif(len(verticles) == 1):
                    verticles.append([lon, lat])
            else:
                verticles.append([lon, lat])

            previous_date = date

        # 最後ラインを作成
        if(len(verticles) > 0):
            w.line([verticles])
            w.record(previous_date)

        # プロジェクションファイル作成
        create_projection_file(output_projection_file_line)

def create_point():
    """ポイント作成"""

    with shapefile.Writer(output_file_point) as w:

        # とりあえず属性は以下の四つとする
        w.field("年月日", "C")
        w.field("時間", "C")
        w.field("緯度", "F", 12, 6)
        w.field("経度", "F", 12, 6)

        # json 読込
        json_open = open(input_file, 'r')
        json_load = json.load(json_open)
        for j in json_load["locations"]:

            timestamp = convert_time(j["timestampMs"])
            lat = float(j["latitudeE7"])/10000000
            lon = float(j["longitudeE7"])/10000000

            w.point(lon, lat)
            w.record(str(timestamp[0]) + "/" + str(timestamp[1]) + "/" + str(timestamp[2]),
                     str(timestamp[3]) + ":" + str(timestamp[4]) + ":" + str(timestamp[5]),
                     lat,
                     lon)

        # プロジェクションファイル作成
        create_projection_file(output_projection_file_point)

if __name__ == '__main__':
    create_point()
    create_line()

このように Shape ファイル(ポイントとライン)に変換することができました。

f:id:sanvarie:20201011224728p:plain

3. ArcGIS Pro で可視化

まずはポイントを可視化してみました。年月日と時間ごとにレコードを作成しました。田端駅周辺にいたことがわかります。ただ、これだと、どういう移動経路なのかがわかりません。

f:id:sanvarie:20201012060246p:plain

そこで、ラインを可視化してみます。以下の画像では2020/10/8は強調されたラインが移動経路になっています。

f:id:sanvarie:20201012060336p:plain

2020/10/10は北区→豊島区→大田区に移動しているのがわかります。

f:id:sanvarie:20201012060432p:plain

さいごに

Googleマップのタイムライン機能を使えばこんなに面白いことが簡単にできてしまうことが分かったかと思います。微妙に場所がずれているのが残念なのと(まぁ仕方ないとは思いますが)、どのタイミングで自分がいた地点が JSON に記録されるのか知れたらもっと楽しそうですね。また、今回も PyShp を使ってみたのですが、サクッと Shape ファイルを作るにはもってこいのライブラリなので、ぜひ使ってみてください。今回は以上です。

ArcObjects からの移行について考えてみる

ArcObjects を使用して GIS アプリケーションを開発している方はたくさんいるかと思います。しかし、Esri は ArcGIS Online などの Web GIS 製品に力を入れており、なおかつ、ArcMap から ArcGIS Pro への移行に伴い、近い将来、ArcObjects のサポートが終了すると言われています。本日はその ArcObjects からの移行について考えてみようと思います。かなり深いテーマであり、本エントリーですべてを網羅できるほど単純な話ではないのは承知なのですが、本件について頭を悩ませている方の参考になれば幸いです。

対象読者

  1. ArcObjects を使用している開発者の方
  2. ArcObjects の代替製品を探している方

ArcMap、 ArcGIS Pro とは

Esri が販売している GIS デスクトップアプリケーションです。それぞれの違いについて以下のエントリーに書いていますので、興味のある方はぜひ読んでみてください。

www.gis-py.com

ArcObjects とは

簡単に言うと、GIS アプリケーションを開発するためのライブラリでしょうか。

blog.esrij.com

Esri の方向性

Esri はプラットフォームとしての ArcGIS の使用を推奨しており、ArcObjects を使用してアプリをスクラッチで開発するというのは時代に合っていないと考えていると個人的には感じています。少し説明しづらいのですが、プラットフォームとしての ArcGIS とは以下に詳細が記載されています。ただ、これを読んでもなかなかピンとこないかと思います。簡単に言うと「Web GIS を利用してデータを共有」「C/S アプリの開発はもうやめて Web GIS を使用」「できるだけアプリの開発はせずに、ArcGIS の既製品を利用」していこうといった感じだと思います。ビジネスのやり方としてはすごく時代に合っていると思います。

www.esrij.com

ArcObjects の代わりとなる製品

単刀直入に言うと代替となる製品はありませんし、現時点でそれを開発をしているという情報もありません。あえて言うなら以下が候補になりうるかと思いますが、今まで ArcObjects で作っていたものをすべて作り直さなければいけません。

ArcGIS Pro SDK

以下エントリーで紹介しているのですが、ArcGIS Pro SDK を使えば ArcGIS Pro の UI をカスタマイズしたり、アドイン機能を開発することができます。

www.gis-py.com

ArcGIS Pro SDK のメリット

  1. ArcGIS Pro をベースにしてアドインを作るだけなので、基本機能は ArcGIS Pro のものを利用することができ、開発工数を減らすことができる。

思いつくのはこのくらいでしょうか。

ArcGIS Pro SDK のデメリット

  1. 高価な ArcGIS Pro を必ず買わなければいけないので費用がかさむ。
  2. ArcGIS Pro ありきなので、ArcGIS Pro が起動していることが前提。そのため、ArcObjects だったらできていたような処理を実現することが難しくなる(タスクでの夜間処理など)
  3. ArcGIS Pro の上にのっかっているアドイン開発なので、ArcGIS Pro を完全に制御することはできない(自由度が低い)。
  4. ArcGIS Pro での何気ない操作が ArcGIS Pro SDK 上でイベントとして走ってしまうことがある(制御が難しい)。

ArcObjects の代替と考えるのは難しいかと思います。

ArcGIS Runtime SDK

こちらも以下エントリーで紹介しているのですが、Windows 及び iOS、Android プラットフォーム上で動作するネイティブ GIS アプリケーションの開発キットです。

www.gis-py.com

ArcGIS Runtime SDK のメリット

  1. クロスプラットフォームでデスクトップアプリとしても開発できるし、スマホアプリとしても開発できる。

メリットとしてはこの辺なのでしょうか。ArcGIS Pro SDK とは異なり、アプリの制御は自分の好きなようにできます。

ArcGIS Runtime SDK のデメリット

  1. Web クライアント的な立ち位置なので、ArcObjects ほど何でもできるというわけではない。特に高頻度で様々なデータを編集するようなアプリの開発には向いていない。
  2. 機能とは関係ないですが、ライセンス形態が複雑すぎて理解することが困難。

こちらも ArcObjects の代替と考えるのは難しいかと思います。

番外:ArcPy

ArcObjects で夜間バッチなどを走らせている場合、上記二つの SDK ではなく、ArcPy を使ってください、と米国Esriもたしか言っていた気がします。

どうやって ArcObjects の代わりとなるアプリを開発すればいいのか

仕様が満たせるのであれば、ArcGIS Pro SDK か ArcGIS Runtime SDK のどちらか適した方で開発すればいいかと思います。ただ、それが難しい場合(ArcGIS Pro SDK も ArcGIS Runtime SDK も単独では ArcObjects の代わりとして使えない場合)、業務や用途に合わせてこれら二つの SDK を使って複数のアプリケーションを開発する必要があるかと思います。ただ、個人的にはこれはあまり現実的ではないかと思います。まず、コストの問題です。ただでさえ ArcGIS Pro は高いのにさらに ArcGIS Runtime SDK 分のコストをかけるというのは難しいかと思います。また、二つの SDK を習得する学習コストや複数のアプリを保守する必要がでてくるので、そこも大きな負担になってくるかと思います。

なぜ ArcObjects の代替となる製品がないのか?

上述したように、Esri はプラットフォームとしての ArcGIS の使用を推奨しており、ArcObjects でアプリを開発するという手法は時代に合わなくなってきていると考えているからだと思います。基本的には Web GIS を中心にできるだけ開発をせずに ArcGIS の既製品を使いましょうというスタンスですね。

ArcGIS の既製品とは何か?

ArcGIS にはデスクトップ製品や開発用の SDK 以外にも様々な製品があります。例えば以下のような製品は ArcGIS のライセンスさえ持っていれば無料で使用することができたはずです。ArcGIS Online や ArcGIS Enterprise などの Web GIS を中心にこういった ArcGIS 製品をできるだけ使用して、開発をできるだけ少なくしていきましょうという方向性になっていますね。

www.esrij.com

doc.arcgis.com

www.esrij.com

Esri の方針は日本の市場に合っているのか?

個人的には合っているとは思いません。Web GIS はまだまだ日本で浸透しているとはいえず、色々な会社さんでスタンドアロン、C/S アプリの開発が行われているのが現状です。日本ではシステムを導入する際、どうしても開発することが前提になってしまうことが多いので、これがプラットフォームとしての ArcGIS の普及が遅れている大きな要因の一つかと思います。また、日本だとシステムを刷新する際も業務フローは意地でも変えず(システムに合わせて業務を変えることはしない)、システムを業務に合わせる傾向にあると思います。そのため、ArcObjects が使われていたのだと思うのですが、プラットフォームとしての ArcGIS を使うためにはシステムに合わせて業務を変える必要が出てくると思います。こういった日本のシステム導入の傾向には Esri の方針は明らかにマッチしていないのではと感じます。

どうして Web GIS がなかなか浸透していないのか?

多くの会社さんが Web GIS に移行したいと考えているかもしれませんが、様々な問題がありそれを実行することができません。

  1. セキュリティ上の理由で ArcGIS Online は使用できない。
  2. ArcGIS Enterprise は非常に高価なので導入できない。

※ArcGIS Online と ArcGIS Enterprise が Web GIS にあたります。

典型的なものとしては上記でしょうか。特に小さな自治体さんなんかに当てはまると思いますが、こうなると Web GIS は使うことができませんね(プラットフォームとしての ArcGIS の使用は難しい)。ですので、こういった自治体さんは ArcObjects で開発したようなスタンドアロンの GIS ソフト を導入せざるを得ません。そのため、開発会社さんもそういった需要に答える必要があり、Web GIS の導入が進まないと考えられます。

ただ、プラットフォームとしての ArcGIS が悪いわけではないかと思います。むしろ、要件にはまればすごく便利だと思います。それが日本での需要にマッチしているかどうかはまた別の話だと思いますが。

ArcGIS ユーザーが他の GIS エンジンに逃げる?

私の知る限りですが、すでにそういう動きをしている会社さんはあります。特に小さな自治体さんなんかを顧客に持つ開発会社さんには「プラットフォームとしての ArcGIS 」はメリットよりデメリットの方が大きいかと思います。 ArcGIS Pro SDK や ArcGIS Runtime SDK では仕様を満たすことが難しいし、コスト的に採用できない。また、これらの SDK で開発するにしても今まで ArcObjects で作ったソフトを作り直ししなければならないのだったらこのタイミングで別の GIS エンジンに乗り換えるというのは自然な流れかと思います。

結局どうすればいいのか?

ArcObjects から移行するにあたって、色々なデメリットがあっても ArcGIS を使い続けたいという方は多いかと思います。そういった方向けに個人的に思う ArcObjects の代替パターンを記載します。

ArcObjects 代替パターン

仕様 ArcGIS Pro SDK ArcGIS Runtime SDK
編集機能がある場合(ポイント、ライン、ポリゴンすべて) ×
編集機能がある場合(アノテーション) ×
編集機能がある場合(ポイントのみ)
参照機能のみの場合※1
高度な印刷機能がある場合※2 ×
ラスターを編集(幾何補正など)する場合 ×
モバイルアプリの場合 ×
アプリを完全に制御したい場合 ×
図面作成をしたい場合 ×
C/S にしたい場合 ×

※1. 仕様次第ですが、高度な分析機能がなければ ArcGIS Runtime SDK で十分かと思います。
※2. テンプレートを使用したり、図面の印刷をする場合など

仕様やコストを考慮しながら決めるものなので、上記の表が絶対というわけではありませんが、参考程度に見ていただければと思います。また、ArcObjects で高度なアプリケーション(様々な編集、分析機能を有しているアプリケーション)を開発している場合、どちらの SDK も不十分となってしまうかもしれません。その場合は、以下二つの選択肢しかないかと思います。

  1. 別の GIS エンジンを使用
  2. アプリの仕様を変更する

まとめ

取り留めの無い話になってしまったかもしれませんが、いかがでしたでしょうか。本件は非常に複雑で、本エントリーですべてを書き切ることは難しいですし、有識者の方からしたら考慮が足りない部分もあるかもしれません。しかし、ArcObjects からの移行に関しては ESRIジャパンさんから具体的なアナウンスがないですし、困っている方がたくさんいらっしゃるかと思います。本エントリーだけでは不十分かと思いますが、そういった方への参考になれば幸いです。もし、何か気になること、知りたいことがありましたらコメントを残していただければと思います。

PyShp を使って CSV をシェープファイルに変換する方法

さて、本日は久しぶりに GIS に関するエントリーを書いてみようと思います。PyShp を使って CSV をシェープファイルに変換してみます。

PyShp とは

Shape ファイルを読み書きするためのライブラリです。

使用するデータ

ESRIジャパンさんが主催した「ArcGIS 開発者のための最新アプリ開発塾 2020」で使用された 各店舗売上.csvを使用します。

community.esri.com

CSV の中身はこんな感じになっています。

f:id:sanvarie:20200930145007p:plain

使用するライブラリ

PyShp と Pandas を使用するのでインストールをお願いします。

実行環境

Windows 10 64bit
Python 3.6.10
Pandas 1.1.2
PyShp 2.1.2
ArcGIS Pro 2.6(作成した Shape ファイルの確認用に使用)

サンプルコード

PyShp を使って CSV をシェープファイルに変換するサンプルです

# -*- coding: utf-8 -*-
import shapefile
import pandas as pd

# 読み込むCSV
input_file = r"D:\python\data\csv\各店舗売上.csv"

# CSV 読込
df = pd.read_csv(input_file, encoding="SHIFT-JIS")

# CSV のカラムを取得
column_list = df.columns.values

def create_shape():

    with shapefile.Writer(r'D:\python\data\pyshp\各店舗売上.shp') as w:

        # Shape のフィールドを作成
        for column in column_list:
            if column == "緯度":
               w.field(column, "F", 12, 6)
            elif column == "経度":
                w.field(column, "F", 12, 6)
            else:
                w.field(column, "C", 50)

        for _, row in df.iterrows():

            lat = 0
            lon = 0
            attributes = []

            for column in column_list:

                if column == "緯度":
                    lat = row[column]
                    attributes.append(lat)

                elif column == "経度":
                    lon = row[column]
                    attributes.append(lon)

                else:
                    attributes.append(row[column])

            # ポイントを作成
            w.point(lon, lat)
            w.record(attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7])

    # プロジェクションファイル作成
    with open("%s.prj" % r'D:\python\data\pyshp\各店舗売上', "w") as prj:
        epsg = 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS 84",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["degree",0.0174532925199433]]'
        prj.write(epsg)

if __name__ == '__main__':
    create_shape()

結果をみるとばっちりシェープファイルが作成されたことがわかります。属性もしっかり付与されてますね。

f:id:sanvarie:20200930200901p:plain

PyShp のいいところ

GDAL や Fiona などで同じことをやろうとすると以下エントリーのように日本語でひっかかるのですが、PyShp の場合はその問題は起きないのが素晴らしいですね。

www.gis-py.com

PyShp のいまいちなところ

属性を作成する際に、w.record(attributes[0],attributes[1],attributes[2],attributes[3],attributes[4],attributes[5],attributes[6],attributes[7]) のようにしなければならないのがいまいちかなと思います(他のやり方があるのかもしれませんが・・・)。この仕様だと汎用的には使えないですね。

解決策

コメント欄参照

最後に

ArcGIS のような商用 GIS を使用すればシェープファイルの作成なんて簡単にできてしまいますが、高価な製品なので簡単には購入できません。シェープファイルが欲しいけど、フリーのライブラリで何とかしたいという方はぜひ PyShp を使ってみてください。本日は以上です。

Python で Tkinter を使って GUI アプリを作ってみよう!

さて、今日は Python で Tkinter を使って GUI アプリを作ってみようと思います。意外と知られていないのですが、Python でも GUI アプリを作ることができます。様々な GUI 系のライブラリがあるのですが、今日は Tkinter というライブラリを使ってみようと思います。

Tkinter とは

Tkinter は Python からGUIを構築・操作するための標準ライブラリ(ウィジェット・ツールキット)です。

作るもの

ジオコーディングをする簡単な GUI アプリです。仕様の詳細は以下を参照してください。

  • 場所を入力
  • 実行ボタンを押下
  • ジオコーディングした結果(緯度経度)を出力

ジオコーディングは geocoder を使おうと思います。geocoder は以下のエントリーでも紹介しているので、興味があったら読んでみてください。

www.gis-py.com

環境

  • Windows 10
  • Python 3.6.6

サンプルコード

ジオコーディングをする GUI アプリのサンプルです。

# -*- coding: utf-8 -*-
from tkinter import*
import geocoder

root = Tk()
root.geometry("300x150")
root.title("ジオコーディングツール")

# フレーム
frame = Frame(root, width = 300, height=150, relief=SUNKEN)
frame.grid()

# ラベル作成
label_location = Label(frame, text="場所", font=('Meiryo' ,12, 'bold'), padx=5, pady=5)
label_location.grid(row=0, column=0, sticky=E)

label_lat = Label(frame, text="緯度", font=('Meiryo' ,12, 'bold'), padx=5, pady=5)
label_lat.grid(row=1, column=0, sticky=E)

label_lon = Label(frame, text="経度", font=('Meiryo' ,12, 'bold'), padx=5, pady=5)
label_lon.grid(row=2, column=0, sticky=E)

# テキストボックス作成
textbox_location = StringVar()
textbox_location_entry = Entry(frame, textvariable=textbox_location, width=30)
textbox_location_entry.grid(row=0, column=1)

textbox_lat = StringVar()
textbox_lat_entry = Entry(frame, textvariable=textbox_lat, width=30)
textbox_lat_entry.configure(state='readonly')
textbox_lat_entry.grid(row=1, column=1)

textbox_lon = StringVar()
textbox_lon_entry = Entry(frame, textvariable=textbox_lon, width=30)
textbox_lon_entry.configure(state='readonly')
textbox_lon_entry.grid(row=2, column=1)

def exexute_geocoding():
    """ジオコーディングを実行するメソッド"""

    # 一度テキストボックスを編集可能にする
    textbox_lat_entry.configure(state='normal')
    textbox_lon_entry.configure(state='normal')

    # ジオコーディング実行
    location = geocoder.osm(textbox_location.get())
    textbox_lat_entry.insert(END, location.latlng[0])
    textbox_lon_entry.insert(END, location.latlng[1])

    # テキストボックスを編集不可にする
    textbox_lat_entry.configure(state='readonly')
    textbox_lon_entry.configure(state='readonly')

def exexute_clear():
    # 一度テキストボックスを編集可能にする
    textbox_lat_entry.configure(state='normal')
    textbox_lon_entry.configure(state='normal')

    textbox_lat_entry.delete(0, END)
    textbox_lon_entry.delete(0, END)
    textbox_location_entry.delete(0, END)

    # テキストボックスを編集不可にする
    textbox_lat_entry.configure(state='readonly')
    textbox_lon_entry.configure(state='readonly')

# ボタン作成
btn_execute = Button(frame, text='実行', command = exexute_geocoding)
btn_execute.grid(row=3, column=1, sticky=E)

btn_clear = Button(frame, text='クリア', command = exexute_clear)
btn_clear.grid(row=3, column=2, sticky=W)

root.mainloop()

こんな感じの見た目になりました。普段 Visual Studio を使っているので、アイテムの配置とかは少し面倒な印象ですね。

f:id:sanvarie:20200919162620p:plain

このように場所を入力します。

f:id:sanvarie:20200919162740p:plain

実行ボタンを押すと、このように緯度経度が出力されます。そして、クリアボタンを押すと項目がクリアされます。

f:id:sanvarie:20200919162812p:plain

まとめ

いかがでしたでしょうか?Python でもこのように GUI アプリを簡単に作成することができます。スクリプトとして実行するだけでも問題ない場合がほとんどかと思いますが、画面で入力できた方が便利な場合もあるかと思います。もし興味がある方はぜひ Tkinter を使ってみてください。本日は以上です。