Labo288

プログラミングのこと、GISのこと、パソコンのこと、趣味のこと

ShapeファイルをGeoJSON形式に変換する

前提

zip圧縮されたシェープファイル群(.shp, .shx, .dbf)を、GeoJson形式で出力する関数です。
当然、シェープファイル群のファイル名が共通している必要があります。
また、.geojsonファイルを出力するのではなく、メモリ上での処理です。
事前にpyshp(shapefile)が必要になります(pip install pyshp)。

コード

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:
		return "Imported file was not apropriate Shape-Zip-File."
	geojson = dict(name=shp_name,
					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定数を定義してある)。