シェープファイルの文字化けの原因をPythonで探る

595
0
2 weeks ago

シェープファイルの文字化けの原因をPythonで探る

はじめに

GIS のソフトウェアを使っているときに、一度は必ず遭遇するのはシェープファイルの文字化けではないでしょうか?

長年、GIS と慣れ親しんでいる方だと対処方法がわかっているので、問題なく対処できると思いますが、最近、大学での授業をお手伝いするなかでも、一度は学生さんから質問される内容の一つです。

対面の場合には、対処方法をその場でお知らせすれば済むのですが、なかなかそうもいかないご時世なのもあり、先日、改めて次の内容をとりまとめたページを弊社の GitHub で公開しました。

  • ArcGIS の dBASE ファイルのコード ページ変換機能 (名称: dbfDefault)
  • ESRI ジャパンで提供している「シェープファイル文字コード設定ユーティリティ 」の役割
  • シェープファイルで文字化けが発生する原因の解説と原因を可視化するサンプル
  • 文字化けへの対処方法として *.cpg ファイルを作成して対処するサンプル

上記の内容を確認したい方は、以下のリンクからアクセスしてください。

シェープファイル または dBASE ファイル の LDID(Language driver ID)と *.cpg ファイル を調べる 」  

また、サンプルとして「ノートブック」  と「Python ツールボックス」  も公開していますので、Python での書き方に興味があるかたは、直接そちらを参照いただくことも可能です。

 

さて、本ブログは開発者向けと名乗っていることもあり、上記とは別のアプローチ「ArcPy がインストールされていないArcGIS API for Python の環境」※で、Shift-JIS のシェープファイルをSpatially enabled DataFrame へ読み込むまでの2つの方法をご紹介します。

Spatially enabled DataFrame って何?という方は、本ブログシリーズで過去に紹介した、「さわって覚える ArcGIS API for Python : Spatially Enabled DataFrame 編 」の解説や、より詳しい解説やサンプルはGuide ページにある、「Part-1 Introduction to Spatially enabled DataFrame」 、「Part-2 Data IO with SeDF - Accessing Data」 、「Part-3 Data IO with SeDF - Exporting Data 」をご参照ください。

※ ArcPy が入っていないArcGIS API for Python の環境は、Installation and using Anaconda for Python Distribution に記載している手順で一から構築した環境や、ArcGIS Online 上のArcGIS Notebook のStandard のランタイムが相当します。

 

ArcPy がインストールされていないArcGIS API for Python の環境でのアプローチ

a) 状況の確認

ArcGIS API for Python を使う場合、シェープファイルをSpatially enable DataFrame に読み込みは、[2.2 Read in local GIS data] - [Reading a Shapefile] でも説明されているように、from_featureclass() を使います。しかし、実際、この関数を使ってArcPy が入っていない環境でShift-JIS のシェープファイルを読み込みすると、エラーになります。

エラーメッセージを観察して見ると、ArcPy が入っていない環境では、shapefile.py を使ってシェープファイルを読み込みしている部分でエラーが発生していることがわかります。この shapefile.pypyshp というライブラリに相当し、ライブラリを確認すると2.0.0 からunicode テキストがフルサポートされ、shapefile.Reader のサンプルであるように、第二引数でencoding を指定できるようになっているようです。

一方、ArcGIS API for Python の from_featureclass() の呼び出し時、encoding の指定は現状は出来ないようです。そうなると別の方法を使う必要がありそうなことがわかります。

 

b) pyshp と shapely を経由して Shift-JIS の シェープファイル をSpatially enable DataFrame に読み込む

幸運なことに、pyshp と shapely を使ってPandas DataFrame に読み込みするコードは、Gist にありました。

Gist(aerispaha/read_shapefile.py)から引用:

 

 

def read_shapefile(shp_path):
	"""
	Read a shapefile into a Pandas dataframe with a 'coords' column holding
	the geometry information. This uses the pyshp package
	"""
	import shapefile

	#read file, parse out the records and shapes
	sf = shapefile.Reader(shp_path)
	fields = [x[0] for x in sf.fields][1:]
	#records = sf.records()
	records =[list(i) for i in sf.records()] #IceMerman のコメントにあるpython 3.6 環境での書換
	shps = [s.points for s in sf.shapes()]

	#write into a dataframe
	df = pd.DataFrame(columns=fields, data=records)
	df = df.assign(coords=shps)

	return df

 

一方、ArcGIS API for Python では、ジオメトリの処理エンジンとしてshapely または arcpy を使うことが説明されていますし、 実際にfrom_shapely() というshapely のジオメトリから、arcgis のジオメトリに変換する関数も存在しています。

そうなると、上記のGist のshapely のジオメトリをarcgis のジオメトリに、from_shapely() を使って変換できれば、最後は、from_df() を使って、Spatially ebaled DataFrame に読み込みすることが実現できそうです。

実際に見つけたコードを少し変更して、Spatially enable DataFrame として返却する関数を作成したものが以下になります。

※1: pyshp は座標系をハンドリングしないようなので、shapely のジオメトリからarcgis のジオメトリに変換する際に座標系を設定しています。

※2: また、コメントとして書いていますがArcGIS API for Python のバージョン2.0.0 を使っている場合、from_shapely で不具合があり、issues に書かれている回避方法を適用する必要があります。

 

# pd.DataFrame.spatial.from_featureclass でArcPyがない場合は、shift-jisのシェープファイルはエラーになる
#
# 回避方法としてPyShp, Shapely, ArcGIS API for Python を使って Spatially enabled DataFrame (SeDF) に変換する
# 
# encodingを「**kwargs」のデータ型は可変長の辞書型(dictionary)で渡すようにすることもできるが今回はオプションにした
# 同じくPyShp はprjファイルは見ていないようなのでwkidもオプションにした
# (wkid: JGD2011:6668 、JGD2000:4612 、WGS84:4326)
def sedf_from_shapefile(shp_path, encoding='utf-8', wkid=4326):
    """
    シェープファイルをPyShp で読み込み、
    ArcGIS API for Python を使って Shapely のジオメトリから arcgis のジオメトリに変換後、
    Spatially enabled DataFrame (SeDF) として返却する関数
    """
    import shapefile
    import pandas as pd
    from arcgis.features import GeoAccessor
    from arcgis.geometry import Geometry
    #read file, parse out the records and shapes
    sf = shapefile.Reader(shp_path, encoding=encoding)#encoding=kwargs['encoding'])
    fields = [x[0] for x in sf.fields][1:]
    records = [list(i) for i in sf.records()]
    
    shps = sf.shapes() # Shapely のジオメトリ
    arcshps = []
    for shp in shps:
        # Convert Shapely Geometry to ArcGIS Geometry; Version 2.0 にアップグレードしたら from_shapely がエラーになる
        # 次期バージョンで修正されるまでは、次の対応をするとエラーは回避可能
        # https://github.com/Esri/arcgis-python-api/issues/1173
        geom = Geometry.from_shapely(shapely_geometry=shp, spatial_reference={'wkid': wkid})
        arcshps.append(geom)

    #write into a dataframe
    df = pd.DataFrame(columns=fields, data=records)
    df = df.assign(SHAPE=arcshps)
    #Spatially enabled DataFrame へ変換
    sdf = pd.DataFrame.spatial.from_df(df, geometry_column='SHAPE')
    #不要なものを削除
    del df, shps, arcshps, records, fields, sf

    return sdf

 

 

あとは、関数に必要なパラメータを指定し、Spatially enable DataFrame に読み込みします。

 

file_path=r"yourlocation\japan_ver83\japan_ver83.shp" #全国市区町村界はShift-JIS

sdf = sedf_from_shapefile(file_path, encoding='shift-jis', wkid=6668)
sdf.head()

 

これでEsri が推奨している、Spatially enable DataFrame でのいろいろな操作が可能な状態になります。

 

c) Geopandas を経由してShift-JIS の シェープファイル をSpatially enable DataFrame に読み込み

また、追加でGeopandas のインストールが必要となりますが、Geopandas が入っている環境であれば、Geopandas のDataFrame から、from_geodataframe() の関数を使って、より簡単にEsri が推奨している、Spatially enable DataFrame へ読み込むことも可能です。

 

import pandas as pd
import geopandas
from arcgis.features import GeoAccessor
file_path = r"yourlocation\japan_ver83\japan_ver83.shp" #全国市区町村界はShift-JIS
geo_df = geopandas.read_file(file_path, encoding='shift-jis') #geopandasはprjファイルも一緒に読み込むみたい
sdf2 = pd.DataFrame.spatial.from_geodataframe(geo_df)
sdf2.head()

 

 

 

おわりに

本記事では、一度は必ず遭遇するシェープファイルの文字化け!

その技術的な解説や原因や対処方法について記載してある、GitHub のページを紹介いたしました。

また、ArcPy が入っていないArcGIS API for Python の環境で、Shift-JIS の シェープファイルをSpatially enabled DataFrame(SeDF) へ読み込みする2つの方法について紹介しました。

Spatially enabled DataFrame(SeDF)の読み込み後の活用方法は、以前、「ArcGIS Field Maps で取得したトラッキングデータの保存とSpatially Enabled DataFrameを試す」でも触れたようにPandas のDataFrame でよく行う操作も可能ですし、米国Esri が提供しているGuide ページにもいろいろございますので、ご参照ください。

 

参考資料

シェープファイル または dBASE ファイル の LDID(Language driver ID)と *.cpg ファイル を調べる 

さわって覚える ArcGIS API for Python : Spatially Enabled DataFrame 編 

Part-1 Introduction to Spatially enabled DataFrame 

Part-2 Data IO with SeDF - Accessing Data 

Part-3 Data IO with SeDF - Exporting Data 

 

Version history
Last update:
2 weeks ago
Updated by:
Contributors