2012年7月12日木曜日

QGis Python Plugin と日本語の周辺1

QGIS の python plugin を書いていて、やはり日本語の処理に四苦八苦したので、判明した事をチビチビと書いていこうと思います。ノウハウが溜まる度に書き足すつもりなので、その1としました。どれぐらいの頻度で続くのかは、書いてる本人にもわかりません。

 文字コードの国際化に関する状況は、刻々と変化する可能性が高いので、環境を明記しときます。
  • Windows 7 (x64)
  • GDAL 1.9.1
  • Quatom GIS Lisboa 1.8.0

 Windows 環境において、GDAL/OGR あたりで、日本語のファイル名を使おうと思ったら、環境変数「GDAL_FILENAME_IS_UTF8」に「NO」と設定しないと、ogr2ogr 等のコマンドでファイル名が不正だとエラーにされてしまう現状があります。

 それなら、思い切ってコントロールパネルの「システム環境設定」に、GDAL_FILENAME_IS_UTF8 = NO と設定してしまえば良いじゃないか?と設定したら、ものの見事に嵌りました。なんと、QGISでshapefileを開こうとするとエラーになってしまいます。なんてこったい!QGIS内部では、UTF8でファイル名を扱っている雰囲気が漂ってます。これに関しては、現段階では確認できていません。

 プラグインのコードで、QFileDialog からファイル名を取得するコードを見てみましょう

    fileName = QFileDialog.getSaveFileName(None,QString.fromLocal8Bit("Select a file:"), "", "*.tapgis")
    if fileName.isNull():
      return
    fname = unicode(fileName.toLocal8Bit(),'mbcs')
  はい、ウィンドウズでは、ファイル名はOEMの文字コードセットになりますので、multi byte char set  つまり、SJIS(CP932)になっております。python なんて、ほとんど知らねぇよだったので、unicode という名前の関数が、文字コード・セットを指定して文字列を初期化する関数だと判るまでに丸一日費やしました。
  さて、こいつを ogr2ogr のコマンドに引き渡すとします。

  実はファイル名に半角スペースが混じっている場合には、”path" というようにダブルクォーテーションでパスを括ってやらなければなりません。逆に半角スペースが混じっていない場合には、path というようにダブルクォーテーションで囲ってはいけません(何この仕様・・・くたばれ)。という事情は、すっ飛ばします。
  # ogrvars には "-overwrite" 等のオプション文字列が渡される
  def CallRWTools(self, infile, outfile, ogrvars):
    import os
    try:
      import subprocess
      path = str(QgsApplication.prefixPath()).rsplit('/', 2)[0]
      pcmd = path + '\\bin\\ogr2ogr';
      fmt = '%s -f "tapgis" %s %s %s'
      cmd = fmt % (pcmd, outfile, infile, ogrvars)
      # ファイル名がUTF8ではないという状態に変更
      os.environ["GDAL_FILENAME_IS_UTF8"] = "NO"
      subprocess.call(cmd.encode('mbcs'), shell=True)
      #cmd3 = "echo " + cmd + " > " + outfile + ".txt"
      #subprocess.call(cmd3.encode('mbcs'), shell=True)
      #cmd2 = "notepad " + outfile + ".txt"
      #subprocess.call(cmd2.encode('mbcs'), shell=True)
    finally:
      # ファイル名がUTF8であるという状態に戻す
      os.environ["GDAL_FILENAME_IS_UTF8"] = "YES"
  はい、QGIS 内部では、GDAL_FILENAME_IS_UTF8 = YES を前提としているので、コマンドを呼び出す間だけ、GDAL_FILENAME_IS_UTF8 = NO に変えてコールします。あと、WIndows シェルでは mbcs なので、encode('mbcs') してやらないと、正しいコマンド引数になりません。

  ここまで書いても、日本語の問題は残ります。今度は、ogr2ogr のオプション文字列が mbcs のままドライバに渡されるためです。自分の書いたドライバでは、sqlite3_open という関数をコールしているので、OGRDriver の OGRDataSource::Open, OGRDataSource::Create という関数内で渡されたファイル名が mbcs なので utf8 へ文字コード変換してやる必要がありました。   また、OGRDataSource::CreateLayer に渡されるレイヤ名は、変換元のデータソースのOGRDriver の実装に依存している状態なので、どんな文字コードが渡されるのか、未知数です。shape driver の実装では、-nln オプションでレイヤ名を指定しない場合には、ファイル名からレイヤ名を生成しているので、mbcs の文字コードが来ました。注意しなければならないのは、これは Windows の日本語OS 独特の状況においてのみ発生します。Windows の Laten1なOSの環境では、おそらく Laten1の文字コードが来るという結果になるでしょう。-nln オプションも生の文字コードが渡されるので、同様の結果になります。

  このように、locale でなんとかしようという発想は、破綻するので utf8 で統一的に扱う必要性が高いと思います。いちいちロケール指定してたら大変です。ウィンドウズの場合は、CPACP を有効活用する方向が望ましいと思います。 2012/07/19 追記: その2に続く 2012/09/05 追記:結局、ウィンドウズ環境では、GDAL_FILENAME_IS_UTF8 = YES を前提にコードが書かれてあるので、その1のアプローチはダメダメでした。

0 件のコメント: