Geokit Gem 1.5和Ruby 1.9.2 => “不兼容的字符编码:UTF-8和ASCII-8BIT”

时间:2010-08-16 12:58:03

标签: ruby-on-rails-3 ruby-1.9 geokit rspec2

我目前正在使用前沿的东西编写一个rails应用程序。 Rails3,rSpec2,Ruby 1.9.2和Geokit 1.5.0。当我尝试对具有非ASCII-8Bit特殊字符的地址进行地理编码时,我收到此错误:

  

不兼容的字符编码:   UTF-8和ASCII-8BIT

Trace就像这样:

1) Spot Basic Validations should calculate lat and lng
    Failure/Error: spot = Spot.create!({
    incompatible character encodings: UTF-8 and ASCII-8BIT
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/geokit-1.5.0/lib/geokit/geocoders.rb:435:in `do_geocode'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/geokit-1.5.0/lib/geokit/geocoders.rb:126:in `geocode'
    # ./app/models/spot.rb:26:in `geocode_address'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activesupport-3.0.0.rc/lib/active_support/callbacks.rb:409:in `_run_validation_callbacks'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activemodel-3.0.0.rc/lib/active_model/validations/callbacks.rb:53:in `run_validations!'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activemodel-3.0.0.rc/lib/active_model/validations.rb:168:in `valid?'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/validations.rb:55:in `valid?'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/validations.rb:75:in `perform_validations'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/validations.rb:49:in `save!'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/attribute_methods/dirty.rb:30:in `save!'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/transactions.rb:242:in `block in save!'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/transactions.rb:289:in `block in with_transaction_returning_status'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/connection_adapters/abstract/database_statements.rb:139:in `transaction'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/transactions.rb:204:in `transaction'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/transactions.rb:287:in `with_transaction_returning_status'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/transactions.rb:242:in `save!'
    # /Users/nilsriedemann/.rvm/gems/ruby-1.9.2-rc2/gems/activerecord-3.0.0.rc/lib/active_record/validations.rb:34:in `create!'
    # ./spec/models/spot_spec.rb:13:in `block (2 levels) in <top (required)>'

我在所有相关文件(规格,工厂和型号)中都使用了# coding: utf-8。但是当我使用像“ElsassersStraße27”这样的地址时,我得到了这个错误。

任何提示?我认为Geokit已经与1.9.1兼容,因此与所有这些新的编码事物兼容。

5 个答案:

答案 0 :(得分:3)

使用CGI.escape不是一个好主意,因为它会产生意想不到的结果。尝试使用和不使用CGI.escape的“挪威奥斯陆”,你会明白我的意思。

更好的解决方案是在位置使用Iconv:

ic = Iconv.new('US-ASCII//IGNORE', 'UTF-8')
utf8location = ic.iconv(location)

干杯!

编辑:我有Wes Gamble的建议在这里进行编辑,我认为这是相关的:

使用//IGNORE将删除所有非ASCII字符。但是在许多(大多数)情况下,你可能想要翻译某些字符,例如变音符号(例如“苏黎世”将成为“苏黎世”)或者carons(例如“Niš”将成为“Nis”)以便成功地对它们进行地理编码。如果忽略非ASCII字符,则“Zürich”将变为“Zrich”,“Niš”将变为“Ni”,两者都不会成功地进行地理编码。

为此你要使用

ic = Iconv.new('US-ASCII//TRANSLIT', 'UTF-8')

请注意,如果无法完成音译,转换将引发异常,因此请确保您处理。

答案 1 :(得分:1)

CGI.escape似乎比Geokit :: Inflector :: url_escape更准确。

以下是编码“ElsassersStraße27”的结果

>> CGI.escape(address)

=> "Elsassers+Stra%C3%9Fe+27"

虽然

>> Geokit::Inflector::url_escape(address)

=> "Elsassers+Stra%C3e+27"

字母ß应显示为c39F(根据http://www.utf8-chartable.de/unicode-utf8-table.pl

此外,调试声明正在爆炸(我知道有理由检查是否启用了调试日志记录:)

所以,这是我对GoogleGeocoder3的解决方案,我猜其他人会有类似的问题

module Geokit
  module Geocoders
    class GoogleGeocoder3 < Geocoder
      def self.do_geocode(address, options = {})
        bias_str = options[:bias] ? construct_bias_string_from_options(options[:bias]) : ''
        address_str = address.is_a?(GeoLoc) ? address.to_geocodeable_s : address
        #use CGI.escape instead of Geokit::Inflector::url_escape
        url ="http://maps.google.com/maps/api/geocode/json?sensor=false&address=#{CGI.escape(address_str)}#{bias_str}"
        res = self.call_geocoder_service(url)
        return GeoLoc.new if !res.is_a?(Net::HTTPSuccess)
        json = res.body
        # escape results of json
        logger.debug "Google geocoding. Address: #{address}. Result: #{CGI.escape(json)}"
        return self.json2GeoLoc(json, address)
      end
    end
  end
end

答案 2 :(得分:0)

您使用的是Postgres和pg gem v0.8吗?升级到0.9

答案 3 :(得分:0)

我知道这是一个非常晚的答案,但我已经为Geokit gem编写了一个Google地理编码器来处理所有这些不兼容错误。此Geocoder使用Google的地理编码服务的最新V3 API。优点是现在它不解析XML,而是更快的JSON,与所需的宝石Yajl(ruby的超快速json解析器)配对更快。我的基准测试显示比旧方法快1.5倍。

https://github.com/rubymaniac/geokit-gem

答案 4 :(得分:-1)

我遇到了同样的问题,我通过添加CGI.escape()解决了这个问题:

geo = Geokit::Geocoders::MultiGeocoder.geocode(CGI.escape(address))