質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.91%

geopandasを用いたシェープファイルの出力で、UnicodeDecodeError(utf-8)またはSchemaError(shift-jis)が発生してしまいます

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 251

tohon

score 9

国土数値情報から土地利用細分メッシュ(リンク内容)をダウンロードし、その中にある区分コードに応じた土地利用種別(リンク内容)をexcelにコピーした表を使い新規フィールドとして追加したシェープファイルを作ろうとしています。フィールドに種別を追加した後で新しいシェープファイルとして出力するという操作を最後に行いたいのですが、そこでエラーが発生してしまいファイルの出力ができず、対策方法が分からず困っております。
(一連の作業はjupyter notebook上で行っております。)

元の土地利用細分メッシュの文字コードはshift-jisで、出力はshift-jisとutf-8のどちらも試しました。

shift-jisで出力しようとしたときはSchemaErrorが出て、別のコードならいけるかもしれないと思いutf-8で出力しようとしたときはUnicodeDecodeErrorが出てしまいどちらでも不可能でした。

エラーコードを見るに元のシェープファイルのフィールド名'メッシュ'に何か問題があるようなのですが、それが何なのかわかっておりません。

以下に作成したコード、shift-jisで出力を試みた時のエラーコード、utf-8で出力を試みた時のエラーコードを記載いたします。

作成したコード

import pandas as pd
import geopandas as gpd
import openpyxl

df1 = gpd.read_file('C:\\Users\\ユーザー名\\土地細分メッシュ\L03-b-16_5339-tky.shp', encoding='shift-jis')
df1['土地利用種'] = df1['土地利用種'].apply(int)
df1['メッシュ'] = df1['メッシュ'].apply(int)

kubun = pd.read_excel('C:\\Users\\ユーザー名\\土地細分メッシュ\\区分.xlsx')
kubun['コード'] = kubun['コード'].apply(int)

df1_code = list(df1['土地利用種'])

kubun_code = list(kubun['コード'])
kubun_name = list(kubun['種別'])

add_kubun = []
for i in df1_code:
    add_kubun.append(kubun_code.index(i))

input_kubun = []
for s in add_kubun:
    input_kubun.append(kubun_name[s])

df1.insert(2, '種別', input_kubun)

df1.to_file('土地利用区分追加.shp', driver='ESRI Shapefile', encoding='shift-jis')

shift-jisで出力しようとしたときのエラーコード

---------------------------------------------------------------------------
CPLE_AppDefinedError                      Traceback (most recent call last)
fiona/ogrext.pyx in fiona.ogrext.WritingSession.start()

fiona/_err.pyx in fiona._err.exc_wrap_int()

CPLE_AppDefinedError: Failed to create field name 'メッシュ': cannot convert to shift-jis

During handling of the above exception, another exception occurred:

SchemaError                               Traceback (most recent call last)
<ipython-input-4-79f9a8b84e47> in <module>
     26 df1.insert(2, '種別', input_kubun)
     27 
---> 28 df1.to_file('土地利用区分追加.shp', driver='ESRI Shapefile', encoding='shift-jis')

C:\ProgramData\Anaconda3\lib\site-packages\geopandas\geodataframe.py in to_file(self, filename, driver, schema, **kwargs)
    502         from geopandas.io.file import to_file
    503 
--> 504         to_file(self, filename, driver, schema, **kwargs)
    505 
    506     def to_crs(self, crs=None, epsg=None, inplace=False):

C:\ProgramData\Anaconda3\lib\site-packages\geopandas\io\file.py in to_file(df, filename, driver, schema, **kwargs)
    126     with fiona_env():
    127         with fiona.open(
--> 128             filename, "w", driver=driver, crs=df.crs, schema=schema, **kwargs
    129         ) as colxn:
    130             colxn.writerecords(df.iterfeatures())

C:\ProgramData\Anaconda3\lib\site-packages\fiona\env.py in wrapper(*args, **kwargs)
    394     def wrapper(*args, **kwargs):
    395         if local._env:
--> 396             return f(*args, **kwargs)
    397         else:
    398             if isinstance(args[0], str):

C:\ProgramData\Anaconda3\lib\site-packages\fiona\__init__.py in open(fp, mode, driver, schema, crs, encoding, layer, vfs, enabled_drivers, crs_wkt, **kwargs)
    261             c = Collection(path, mode, crs=crs, driver=driver, schema=this_schema,
    262                            encoding=encoding, layer=layer, enabled_drivers=enabled_drivers, crs_wkt=crs_wkt,
--> 263                            **kwargs)
    264         else:
    265             raise ValueError(

C:\ProgramData\Anaconda3\lib\site-packages\fiona\collection.py in __init__(self, path, mode, driver, schema, crs, encoding, layer, vsi, archive, enabled_drivers, crs_wkt, ignore_fields, ignore_geometry, **kwargs)
    160             elif self.mode in ('a', 'w'):
    161                 self.session = WritingSession()
--> 162                 self.session.start(self, **kwargs)
    163         except IOError:
    164             self.session = None

fiona/ogrext.pyx in fiona.ogrext.WritingSession.start()

SchemaError: Failed to create field name 'メッシュ': cannot convert to shift-jis

utf-8で出力しようとしたときのエラーコード

---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe3 in position 61: invalid continuation byte

Exception ignored in: 'fiona._env.log_error'
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe3 in position 61: invalid continuation byte
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 in position 64: invalid continuation byte

Exception ignored in: 'fiona._env.log_error'
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe7 in position 64: invalid continuation byte
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe6 in position 64: invalid continuation byte

Exception ignored in: 'fiona._env.log_error'
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe6 in position 64: invalid continuation byte
---------------------------------------------------------------------------
UnicodeDecodeError                        Traceback (most recent call last)
<ipython-input-3-c48df3a82a00> in <module>
     26 df1.insert(2, '種別', input_kubun)
     27 
---> 28 df1.to_file('土地利用区分追加.shp', driver='ESRI Shapefile', encoding='utf-8')

C:\ProgramData\Anaconda3\lib\site-packages\geopandas\geodataframe.py in to_file(self, filename, driver, schema, **kwargs)
    502         from geopandas.io.file import to_file
    503 
--> 504         to_file(self, filename, driver, schema, **kwargs)
    505 
    506     def to_crs(self, crs=None, epsg=None, inplace=False):

C:\ProgramData\Anaconda3\lib\site-packages\geopandas\io\file.py in to_file(df, filename, driver, schema, **kwargs)
    126     with fiona_env():
    127         with fiona.open(
--> 128             filename, "w", driver=driver, crs=df.crs, schema=schema, **kwargs
    129         ) as colxn:
    130             colxn.writerecords(df.iterfeatures())

C:\ProgramData\Anaconda3\lib\site-packages\fiona\env.py in wrapper(*args, **kwargs)
    394     def wrapper(*args, **kwargs):
    395         if local._env:
--> 396             return f(*args, **kwargs)
    397         else:
    398             if isinstance(args[0], str):

C:\ProgramData\Anaconda3\lib\site-packages\fiona\__init__.py in open(fp, mode, driver, schema, crs, encoding, layer, vfs, enabled_drivers, crs_wkt, **kwargs)
    261             c = Collection(path, mode, crs=crs, driver=driver, schema=this_schema,
    262                            encoding=encoding, layer=layer, enabled_drivers=enabled_drivers, crs_wkt=crs_wkt,
--> 263                            **kwargs)
    264         else:
    265             raise ValueError(

C:\ProgramData\Anaconda3\lib\site-packages\fiona\collection.py in __init__(self, path, mode, driver, schema, crs, encoding, layer, vsi, archive, enabled_drivers, crs_wkt, ignore_fields, ignore_geometry, **kwargs)
    160             elif self.mode in ('a', 'w'):
    161                 self.session = WritingSession()
--> 162                 self.session.start(self, **kwargs)
    163         except IOError:
    164             self.session = None

fiona/ogrext.pyx in fiona.ogrext.WritingSession.start()

fiona/ogrext.pyx in fiona.ogrext.Session.get_schema()

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe3 in position 9: unexpected end of data
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

エラーコードを見るに元のシェープファイルのフィールド名'メッシュ'に何か問題があるようなのですが、それが何なのかわかっておりません。

内部で使用しているGDALのAPIに問題があるようです。調べた結果、メッシュの最後の文字がNGです。最後の行のdf1.to_file()の前に以下のコードを入れてメッシュを「メッシ」にすると動くと思います。

columns = df1.columns.tolist()
columns[columns.index('メッシュ')] = 'メッシ'
df1.columns = columns

[追記]
Windowsでは上記の対策ではNGでしたので、シェープファイルを作るだけであれば下記の対応で可能です(根本的な解決ではないです)。上記の代わりにフィールド名を全て英語名に変換します。

df1.columns = ["mesh", "utilization", "type", "datetime", "geometry"]


その上で書き出す際にutf-8を指定して下さい。

df1.to_file("土地利用区分追加.shp", driver="ESRI Shapefile", encoding="utf-8")

参考情報

C++から直接GDALのAPIを呼び出しても同じようにがデコードされません。下記のソースコードはシェープファイルにフィールド名メッシュをUTF-8指定で作成し、読み出すだけのプログラムです。

#include <iostream>
#include <iomanip>
#include <string>
#include "gdal.h"
#include "ogr_api.h"
#include "ogr_p.h"

void dump_utf8(const std::string &s)
{
    std::cout << s << ":";
    for (std::string::size_type i = 0; i < s.length(); ++i)
    {
        int val = (int)(s[i]) & 0xFF;
        std::cout << " 0x";
        std::cout << std::hex << std::setfill('0') << std::setw(2);
        std::cout << val;
    }
    std::cout << std::endl;
}

int main()
{
    std::string s = u8"メッシュ";
    dump_utf8(s);

    // initialize
    OGRRegisterAll();
    OGRSFDriverH drv = OGRGetDriverByName("ESRI Shapefile");
    OGRDataSourceH ds = OGR_Dr_CreateDataSource(drv, "test.shp", nullptr);

    // options
    char **options = nullptr;
    options = CSLSetNameValue(options, "ENCODING", "UTF-8");

    // layer
    OGRSpatialReferenceH srs = OSRNewSpatialReference(nullptr);
    OGRwkbGeometryType geom_type = static_cast<OGRwkbGeometryType>(1);
    OGRLayerH layer = GDALDatasetCreateLayer(ds, "test", srs, geom_type, options);

    // field
    OGRFieldDefnH field = OGR_Fld_Create(s.c_str(), OFTString);
    OGRErr err = OGR_L_CreateField(layer, field, true);

    // check
    OGRFeatureDefnH feature = OGR_L_GetLayerDefn(layer);
    OGRFieldDefnH field_out = OGR_FD_GetFieldDefn(feature, 0);
    std::string s_out = OGR_Fld_GetNameRef(field_out);
    dump_utf8(s_out);

    // release
    OGR_Fld_Destroy(field);
    CSLDestroy(options);
    OSRDestroySpatialReference(srs);
    OGRReleaseDataSource(ds);

    return 0;
}


結果

$ g++ --std=c++11 -o test_gdal test_gdal.cc -I /Users/guest/miniconda3/envs/geo/include -L /Users/guest/miniconda3/envs/geo/lib -lgdal
$ DYLD_LIBRARY_PATH=/Users/guest/miniconda3/envs/geo/lib ./test_gdal
メッシュ: 0xe3 0x83 0xa1 0xe3 0x83 0x83 0xe3 0x82 0xb7 0xe3 0x83 0xa5
Warning 6: Normalized/laundered field name: 'メッシュ' to 'メッシ�'
メッシ�: 0xe3 0x83 0xa1 0xe3 0x83 0x83 0xe3 0x82 0xb7 0xe3

バグの発生箇所

UTF-8を指定した際のバグの発生箇所を特定しました。
https://github.com/OSGeo/gdal/blob/master/gdal/ogr/ogrsf_frmts/shape/shapefil.h
の515行目で下記の定義がされています。

#define XBASE_FLDNAME_LEN_WRITE 10


これはフィールド名に使えるのは10バイトであることを意味しています。「メッシュ」はUTF-8では12バイト(1文字3バイト)、SJISでは8バイト(1文字2バイト)になります。したがってUTF-8を指定した場合、10文字制限で最後の2バイトが切り取られ、文字列として不完全になりエラーとなっていました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/08 13:55

    早速ありがとうございます!
    変更後の方法、
    columns = df1.columns.tolist()
    columns[columns.index('メッシュ')] = 'メッシ'
    df1.columns = columns
    で試してみますと、今度は書き換えた「メッシ」で最初と同じエラーが発生しています。
    SchemaError: Failed to create field name 'メッシ': cannot convert to shift-jis
    ここは他に様々な語(「1」、「mesh」等)で置き換えを試しましたが結果は同様でした。
    df1.to_fileでファイルを出力する前にprint(df1.head())でカラム名を調べると、ちゃんと名前は置き換わっているようです。シェープファイルへの書き出しの段階でおっしゃっていただいている「GDALに異常がある」ということの影響を受けているのでしょうか。

    キャンセル

  • 2020/07/08 18:10

    macOSとLinuxで上記の対応で動作しましたが、確かにWindowsで行ったところ同エラーが発生しました。OSによる差もあるようです。対策はさらに追記します。

    キャンセル

  • 2020/07/09 10:55 編集

    バグの発生個所と原因を特定いただきまして、誠にありがとうございます。何が原因で起きているのかまったくわからずの状態でありましたので、突き止めていただけて深く感謝いたします。
    申し遅れてしまい恐縮ですが、作業は全てwindowsで行っていましたのでおっしゃる通りこのエラーがずっと発生している状態になっていました。
    また、英語名で置き換える方法で目的のファイルを作成することができ、安心いたしました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.91%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Pythonに関する質問
  • geopandasを用いたシェープファイルの出力で、UnicodeDecodeError(utf-8)またはSchemaError(shift-jis)が発生してしまいます