Posted at

シェープファイルをGeoJSON形式に変換する


前提

zip圧縮されたシェープファイル群(.shp, .shx, .dbf)を、GeoJson形式で出力する関数です。

当然、シェープファイル群のファイル名が共通している必要があります。

また、.geojsonファイルを出力するのではなく、メモリ上で処理します。

事前にpyshp(shapefile)が必要になります。


コード

import shapefile, io, zipfile

#zipファイルからshpファイルを探してファイル名を取得
def find_shpfile_inzip(zipped_shp):
zip_infolist = zipped_shp.infolist()
for info in zip_infolist:
if not info.filename.startswith('__MACOSX/'): #Mac-ZIP対応
if info.filename.endswith('.shp'):
return info.filename

まずこのように、zip圧縮されたshapefileのファイル名を取得する関数を定義します。

この関数を用いて

#ZIP保存されたSHP群をGEOJSON形式で返す

def zipped_shp_to_geojson(zipped_shp):
zipped_files = zipfile.ZipFile(zipped_shp)

#zipファイルからshpファイルを探してファイル名を取得
shp_name = find_shpfile_inzip(zipped_files)[:-4]

#shapefile読み込みと適合判定
try:
shp_file_bytes = zipped_files.read(shp_name + '.shp')
shx_file_bytes = zipped_files.read(shp_name + '.shx')
dbf_file_bytes = zipped_files.read(shp_name + '.dbf')
except:
print("Imported file was not apropriate Shape-Zip-File.")
return None

geojson = dict(type="FeatureCollection", features=[])
#pyshpはライブラリ内部でデコードするので、正しいエンコーディングでデコード出来るまでループ
for codec in CODECS:
try:
reader = shapefile.Reader(shp=io.BytesIO(shp_file_bytes),
shx=io.BytesIO(shx_file_bytes),
dbf=io.BytesIO(dbf_file_bytes),encoding=codec)

fields = reader.fields[1:]
field_names = [field[0] for field in fields]
for sr in reader.shapeRecords():
atr = dict(zip(field_names, sr.record))
geom = sr.shape.__geo_interface__
geojson['features'].append(dict(type="Feature", \
geometry=geom, properties=atr))
print(codec + 'encoding is correct.' )
break
except UnicodeDecodeError:
print(codec + 'is not suitable for this file.')
continue
return geojson

これでgeojsonに変換されます。

エンコーディングについてはコメントのとおりです。pyshpではデフォルトだとUTF-8でデコードされますが、オープンデータで提供されるシェープファイルは、大半がcp932(Windows環境のShift-JIS)です。エンコーディングの指定が間違っているとUnicode errorが発生します。それならcp932を指定してデコードすれば良いのでは、と考えますが、当然ながらUTF-8等にも対応しておきたいところです。したがって、メジャーなエンコーディングのなかから適合するまでデコードし続けるようになっています(このモジュールの冒頭で、CODECS定数を定義してある)。