読者です 読者をやめる 読者になる 読者になる

reverse geocodingやるか?

FriendFeedの記事が発端。
携帯端末から快適に使うとなると、まず簡易位置情報を取得するとこも鍵だけど、専用のviewを追加する方法もよくわかってない俺なので、reverse geocodingの方から手をつけてみる。
随時修正、追加の予定。
(2009/3/5追記:とりあえずいい加減だが実装してみた
f2pのコードを見てみると、現在正引きに実際に使ってるのは
http://www.geocoding.jp/
なのだった!
じゃあ、app/controllers/entry_controller.rbでは引き方によって違うgeocoderを作るのか。

class EntryController < ApplicationController
(snip)

  def new
(snip)
    if validgeocodes?(@lat,@long)
      geocoder = GoogleMaps::GoogleGeocoder.new(http_client)
      @placemark = geocoder.reversesearch(@lat, @long)
      if @placemark and !@placemark.ambiguous?
        @address = @placemark.address
      end
    elseif @title
      geocoder = GoogleMaps::GeocodingJpGeocoder.new(http_client)
      @placemark = geocoder.search(@title)
      if @placemark and !@placemark.ambiguous?
        @address = @placemark.address
        @lat = @placemark.lat
        @long = @placemark.long
      end
    end
  end

validgeocodes?のところは、Pointオブジェクトを生成してみてチェックする?でもそれは変だな。
@latと@longが共にnilもしくはundefinedの場合と緯度0,経度0の区別はちゃんとできるんかな?(←Rubyをよく知らない俺)

で、Googleからのレスポンスを見てみた

{"Status"=>{"code"=>200, "request"=>"geocode"},
 "name"=>"wallstreet, New York city",
 "Placemark"=>
  [{"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"LocalityName"=>"New York", "AddressLine"=>["Wall Street"]},
          "AdministrativeAreaName"=>"NY"},
        "CountryName"=>"USA",
        "CountryNameCode"=>"US"},
      "Accuracy"=>4},
    "id"=>"p1",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>-74.003703,
        "south"=>40.699406,
        "west"=>-74.018054,
        "north"=>40.712064}},
    "Point"=>{"coordinates"=>[-74.0109161, 40.7059266, 0]},
    "address"=>"Wall Street, New York, NY, USA"}]}

これは最初から結果が1つに絞られていて、あいまいじゃないケース。
問題は日本語とか、広い地域の場合

{"Status"=>{"code"=>200, "request"=>"geocode"},
 "name"=>"神奈川県茅ヶ崎市",
 "Placemark"=>
  [{"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>{"LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>4},
    "id"=>"p1",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4440042,
        "south"=>35.3148695,
        "west"=>139.3687825,
        "north"=>35.3847785}},
    "Point"=>{"coordinates"=>[139.4047163, 35.3338596, 0]},
    "address"=>"Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"DependentLocality"=>{"DependentLocalityName"=>"茅ヶ崎"},
            "LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>4},
    "id"=>"p2",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4109407,
        "south"=>35.3311558,
        "west"=>139.3959875,
        "north"=>35.3417475}},
    "Point"=>{"coordinates"=>[139.403769, 35.3355955, 0]},
    "address"=>"茅ヶ崎, Chigasaki, Kanagawa Prefecture, Japan"}]}

ではreverse geocodingしてみるべ

queryはこんな感じ(xmlはうまくparseできなかった)

{

      query = {
        'll' => lat + ',' + long,
        'output' => 'json',
        'oe' => oe,
        'key' => @key
      }

 "oe"=>"utf-8",
 "ll"=>"35.3338596,139.4047",
 "output"=>"json",
 "key"=>  ""
}

帰ってきたデータxmlはこんなの

# {http://earth.google.com/kml/2.0}Placemark=[#>>>>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>>>>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>>>>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>>>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>, #>> {http://earth.google.com/kml/2.0}ExtendedData=#> {http://earth.google.com/kml/2.0}Point=#>]>>

JSON版はこんなの。たくさんの候補が帰ってくるが、いずれにしても、住所のスタイルがleafからrootへ向かう米国式スタイルなので、そのままは使えない。GeocodingJPの結果を使いたくなる気持ちはわかる。

{"Status"=>{"code"=>200, "request"=>"geocode"},
 "name"=>"35.333860,139.404700",
 "Placemark"=>
  [{"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"DependentLocality"=>
             {"Thoroughfare"=>{"ThoroughfareName"=>"1丁目1−1"},
              "DependentLocalityName"=>"茅ヶ崎"},
            "LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>8},
    "id"=>"p1",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.40785,
        "south"=>35.3307314,
        "west"=>139.4015548,
        "north"=>35.3370266}},
    "Point"=>{"coordinates"=>[139.4047024, 35.333879, 0]},
    "address"=>
     "1丁目1 1茅ヶ崎, Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"CountryName"=>"Japan",
        "PostalCode"=>{"PostalCodeNumber"=>"253-8686"},
        "CountryNameCode"=>"JP"},
      "Accuracy"=>5},
    "id"=>"p2",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4078722,
        "south"=>35.3306703,
        "west"=>139.401577,
        "north"=>35.3369655}},
    "Point"=>{"coordinates"=>[139.4047246, 35.3338179, 0]},
    "address"=>"〒253-8686"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"DependentLocality"=>
             {"Thoroughfare"=>{"ThoroughfareName"=>"1丁目1"},
              "DependentLocalityName"=>"茅ヶ崎"},
            "LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>7},
    "id"=>"p3",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4078959,
        "south"=>35.3307155,
        "west"=>139.4016006,
        "north"=>35.3370107}},
    "Point"=>{"coordinates"=>[139.4047524, 35.3341457, 0]},
    "address"=>"1丁目1茅ヶ崎, Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"DependentLocality"=>
             {"Thoroughfare"=>{"ThoroughfareName"=>"1丁目"},
              "DependentLocalityName"=>"茅ヶ崎"},
            "LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>4},
    "id"=>"p4",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4056645,
        "south"=>35.3303333,
        "west"=>139.3993692,
        "north"=>35.3366286}},
    "Point"=>{"coordinates"=>[139.403483, 35.3339456, 0]},
    "address"=>"1丁目茅ヶ崎, Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>
           {"DependentLocality"=>{"DependentLocalityName"=>"茅ヶ崎"},
            "LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>4},
    "id"=>"p5",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4109407,
        "south"=>35.3311558,
        "west"=>139.3959875,
        "north"=>35.3417475}},
    "Point"=>{"coordinates"=>[139.403769, 35.3355955, 0]},
    "address"=>"茅ヶ崎, Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"Locality"=>{"LocalityName"=>"Chigasaki"},
          "AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>4},
    "id"=>"p6",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.4440042,
        "south"=>35.3148695,
        "west"=>139.3687825,
        "north"=>35.3847785}},
    "Point"=>{"coordinates"=>[139.4047163, 35.3338596, 0]},
    "address"=>"Chigasaki, Kanagawa Prefecture, Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"CountryName"=>"Japan",
        "CountryNameCode"=>"JP",
        "AddressLine"=>["Kanagawa"]},
      "Accuracy"=>2},
    "id"=>"p7",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.796539,
        "south"=>35.128529,
        "west"=>138.915863,
        "north"=>35.673813}},
    "Point"=>{"coordinates"=>[139.1221414, 35.4252852, 0]},
    "address"=>"Kanagawa Japan"},
   {"AddressDetails"=>
     {"Country"=>
       {"AdministrativeArea"=>
         {"AdministrativeAreaName"=>"Kanagawa Prefecture"},
        "CountryName"=>"Japan",
        "CountryNameCode"=>"JP"},
      "Accuracy"=>2},
    "id"=>"p8",
    "ExtendedData"=>
     {"LatLonBox"=>
       {"east"=>139.8359019,
        "south"=>35.1286233,
        "west"=>138.9157485,
        "north"=>35.6727538}},
    "Point"=>{"coordinates"=>[139.6423446, 35.4475073, 0]},
    "address"=>"Kanagawa Prefecture, Japan"}]}

簡易位置情報の取得

まずはページ遷移を考える。一番安易かつspeedyなのは、メインの画面から直接枝分かれする方法。紛らわしいアイコンが増えていく、、、(settingsでデフォルトのフォームを変更するという方向もあるだろう)

単純にやってみると、こんなGET requestがくる

"GET /entry/new?datum=1&unit=0&lat=35.19.30.0&lon=139.20.35.25 HTTP/1.1" 200 2668

あー、度、分、秒の単位系なのだった。これを変換しないとあかんのか。

埋め込むURLの造り方が微妙だな。
あんまりsettingsを細分化させるのはどうかと思うが、リンク作る現場でUserAgent見るべきとも思えない。(別の携帯、PCには別のsettingsがあるわけで)

すごくやっつけhackな方法

簡易位置情報をGETでとりこんで、分秒の単位を変換したら、GeocodingJPにそのまま投げちゃうってのはどうだろう。どっちみちGoogleのreverse geocodingの結果はそのまま使うのは微妙なのだし、、、うん、そうしよう。(2009/3/6 5:36a.m.ココ)←(2009/03/6 10:00追記)でも、reverse geocodingやるか? - kgbu?ではいちおうGoogleのgeocoderつかってreverse geocodingしてみている。