世界の測量

Sibling of "Relevant, Timely, and Accurate, " but much lighter and shorter ※自らの所属する組織の見解を示すものでない

基盤地図情報Shapefileの文字コードをShift_JISからUTF-8に変換するコード

【2013-12-26: GeoRuby がアップデートされたのでこのエントリの内容は古くなっている。部分的にアップデートしたエントリを GeoRubyによるShapefileの文字コードのShapefileからUTF-8への変換(Late 2013) - 世界の測量 に上げている。】

次のようなコードとなる。ただし、DBFヘッダフィールド名は8バイトに固定されているので、Shift_JISからUTF-8に変更した時におおむねバイト数が1.5倍になることから、「存在期間自」といったフィールド名についてバイト列が途中で切れてしまって、文字列の後ろが壊れる。QGISでもTileMillでも、壊れた形の文字列がヘッダ行に入っているように表示されるので間違いないだろう。幸いなことに、「名称」といった利用のしがいがある属性については属性名が短いので、割と悪影響は少ない。

データのフィールド長は余裕を持って2倍に拡張するようにしてあるので、実データについては切れてしまうことはないと思う。

# convert.rb cc0
require 'find'
require 'rubygems'
require 'geo_ruby'
require 'iconv'
c = Iconv.new('UTF-8', 'CP932')

include GeoRuby::Shp4r
Find.find('.') {|src_path|
  next if /utf8/.match src_path
  next unless /shp$/.match src_path
  dst_path = src_path.sub(/^\./, 'utf8')
  #next if File.exist?(dst_path)
  ShpFile.open(src_path) {|src|
    dst_fields = []
    src.fields.each {|field|
      print c.iconv(field.name), "\n"
      dst_fields << 
        Dbf::Field.new(c.iconv(field.name),
          field.type, field.length * 2, field.decimal)
    }
    ShpFile.create(dst_path, src.shp_type, dst_fields) {|dst|
      dst.transaction {|tr|
        src.each {|r|
          d = {}
          r.data.each{|k, v|
            d[c.iconv(k)] = (v.class == String) ? c.iconv(v) : v
          }
          tr.add(ShpRecord.new(r.geometry, d))
        }
      }
    }
  }
}

ブランクがあいてしまったので、随分リズム感のないコードになっていると思う。