reverse geocodingとau携帯の簡易位置情報取得のテストしてみた(2009/3/8にgithubにforkしたf2pを上げました)

f2p@githubのコードをいじってます。
PCからテストするには /f2p/entry/new?lat=35.19.30.0&lon=139.20.35.25 とかいう風にURLを入力する。(経度、緯度は分、秒、100分の1秒の単位らしい(あ、計算まちごうとった、ので直した)

実装の問題点

  • calc_geo : 度、分、秒のデータを変換するルーチンの置き場所として適当なところがわからない
  • au以外の端末に対して、どうやって対処するか。
    • 現状はsettingsにはuse GPS infoのチェックボックスがあるだけだが、それをキャリアごとにするか?それともUserAgentを自動判別するか
    • 簡易位置情報を利用してのpostのリンクをworld_addのアイコンで表示しているが、そこに張られるリンクはキャリアによって異なる。これはどこで判断させるか。settingsにキャリアの種別があればそれを使うが、app/helpers/application_helper.rb の中でUserAgentを見て云々というのはしたくない。
  • app/helpers/application_helper.rbのwrite_with_geocode_linkがとてもhard codingで哀しい。とはいえ、どうやって device:location?url=... とかいうリンクを作れっちゅうねん→ link_to

2009/3/8にgithubにf2pをforkしたものをあげました

kgbu/f2p · GitHub
でも、jpmobileとかを使ってないので、大幅に書き換えるかもしれません。

記事投稿時点での状況など

とりあえず現状のgit statuやgit diff(config/environment.rbは関係ないkeyのところとかを消してます)
(2009/3/5 10:22現在、通常のgeocodingについてはGeocodingJPのサービスを使うように戻しました。よって app/controllers/entry_controller.rbのdiffの中身も替えてます)

まずgit statusの中から

# On branch master
# Changed but not updated:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   app/controllers/entry_controller.rb
#       modified:   app/controllers/setting_controller.rb
#       modified:   app/helpers/application_helper.rb
#       modified:   app/models/setting.rb
#       modified:   app/views/entry/new.html.erb
#       modified:   app/views/layouts/entry.html.erb
#       modified:   app/views/setting/index.html.erb
#       modified:   config/environment.rb
#       modified:   config/environments/development.rb
#       modified:   config/environments/production.rb
#       modified:   lib/google_maps.rb

そしてgit diff

diff --git a/app/controllers/entry_controller.rb b/app/controllers/entry_controller.rb
index 1786721..a57261d 100644
--- a/app/controllers/entry_controller.rb
+++ b/app/controllers/entry_controller.rb
@@ -257,11 +257,39 @@ class EntryController < ApplicationController
     @link = param(:link)
     @with_form = param(:with_form)
     @title = param(:title)
+    @lon = param(:lon)
     @lat = param(:lat)
-    @long = param(:long)
+
+    def calc_geo(m_s_sss)
+      sign = ""
+      if /([-])?(\d{1,3})\.(\d{1,2})\.(\d{1,2})\.(\d{1,3})/ =~ m_s_sss then
+        sign = $1
+        m_s_sss = (((($5.to_f/100) + $4.to_f)/60 + $3.to_f)/60 + $2.to_f)
+        if sign == "-" then
+          (m_s_sss * -1 ).to_s
+        else
+          m_s_sss.to_s
+        end
+      else
+        m_s_sss
+      end
+    end
+
+    @lon = calc_geo(@lon)
+    @lat = calc_geo(@lat)
+
+    @long = param(:long) || @lon
     @address = param(:address)
     @placemark = nil
-    if @title
+    if @lon
+      geocoder = GoogleMaps::GoogleGeocoder.new(http_client, F2P::Config.google_maps_api_key)
+      @placemark = geocoder.reversesearch(@lat, @lon)
+p @lon
+p @lat
+      if @placemark and !@placemark.ambiguous?
+        @address = @placemark.address
+      end
+    elsif @title
       geocoder = GoogleMaps::GeocodingJpGeocoder.new(http_client)
       @placemark = geocoder.search(@title)
       if @placemark and !@placemark.ambiguous?
@@ -314,6 +342,7 @@ class EntryController < ApplicationController
     file = param(:file)
     @lat = param(:lat)
     @long = param(:long)
+    @lon = param(:lon)
     @title = param(:title)
     @address = param(:address)
     opt = create_opt(:room => @ctx.room)
diff --git a/app/controllers/setting_controller.rb b/app/controllers/setting_controller.rb
index d172715..b0b8e5f 100644
--- a/app/controllers/setting_controller.rb
+++ b/app/controllers/setting_controller.rb
@@ -15,12 +15,13 @@ class SettingController < ApplicationController
     @list_view_media_rendering = param(:list_view_media_rendering) || @setting.list_view_media_rendering
     @link_open_new_window = param(:link_open_new_window) || @setting.link_open_new_window
     @link_type = param(:link_type) || @setting.link_type
+    @use_gps_info = param(:use_gps_info) || @setting.use_gps_info
   end

   def update
     updated = false
     original_value = {}
-    [:font_size, :entries_in_page, :entries_in_thread, :text_folding_size, :list_view_media_rendering, :link_open_new_window, :link_type].each do |key|
+    [:font_size, :entries_in_page, :entries_in_thread, :text_folding_size, :list_view_media_rendering, :link_open_new_window, :link_type, :use_gps_info].each do |key|
       original_value[key] = @setting.send(key)
     end
     # int settings
@@ -37,6 +38,7 @@ class SettingController < ApplicationController
     if param(:link_type_gwt) == 'checked'
       @setting.link_type = 'gwt'
     end
+    @setting.use_gps_info = (param(:use_gps_info) == 'checked')
     if errors = @setting.validate
       original_value.each do |key, value|
         @setting.send(key.to_s + '=', value)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 551bf29..8b9d1f8 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -33,6 +33,7 @@ module ApplicationHelper
     'pin' => 'anchor.png',
     'bottom' => 'arrow_down.png',
     'top' => 'arrow_up.png',
+    'world_add' => 'world_add.png',
   }

   def icon_url(name)
@@ -61,6 +62,12 @@ module ApplicationHelper
     link_to(icon_tag(:logout), :controller => 'login', :action => 'clear')
   end

+  def write_with_geocode_link
+    if setting.use_gps_info
+      '<a href="device:location?url=new"><img alt="world add" height="16" src="/images/icons/world_add.png" title="world add" width="16" /></a>'
+    end
+  end
+
   def u(arg)
     if arg
       super(arg)
diff --git a/app/models/setting.rb b/app/models/setting.rb
index f302bd9..02a6759 100644
--- a/app/models/setting.rb
+++ b/app/models/setting.rb
@@ -5,6 +5,7 @@ class Setting
   attr_accessor :text_folding_size
   attr_accessor :link_open_new_window
   attr_accessor :link_type
+  attr_accessor :use_gps_info
   attr_accessor :list_view_media_rendering

   def initialize
@@ -15,6 +16,7 @@ class Setting
     @text_folding_size = F2P::Config.text_folding_size
     @link_open_new_window = F2P::Config.link_open_new_window
     @link_type = F2P::Config.link_type
+    @use_gps_info = F2P::Config.use_gps_info
     @list_view_media_rendering = F2P::Config.list_view_media_rendering
   end

diff --git a/app/views/entry/new.html.erb b/app/views/entry/new.html.erb
index 2f1cd71..d778e8d 100644
--- a/app/views/entry/new.html.erb
+++ b/app/views/entry/new.html.erb
@@ -11,6 +11,7 @@
     <%- end -%>
     <%= hidden_field_tag('lat', @lat) %>
     <%= hidden_field_tag('long', @long) %>
+    <%= hidden_field_tag('lon', @lon) %>
     <%= hidden_field_tag('title', @title) %>
     <%= hidden_field_tag('address', @address) %>
     <%= submit_tag 'post' %>
diff --git a/app/views/layouts/entry.html.erb b/app/views/layouts/entry.html.erb
index 73ee3a9..8908281 100644
--- a/app/views/layouts/entry.html.erb
+++ b/app/views/layouts/entry.html.erb
@@ -31,7 +31,7 @@
     </style>
   </head>
   <body>
-    <h1><%= link_to(h(appname), :controller => 'entry', :action => 'list') %> <%= h(viewname) %> at <a name="top"><%= date(Time.now) %></a> <%= write_new_link %> <%= search_link %> <%= settings_link %> <%= logout_link %></h1>
+    <h1><%= link_to(h(appname), :controller => 'entry', :action => 'list') %> <%= h(viewname) %> at <a name="top"><%= date(Time.now) %></a> <%= write_new_link %> <%= write_with_geocode_link %> <%= search_link %> <%= settings_link %> <%= logout_link %></h1>
     <%= yield %>
   </body>
 </html>
diff --git a/app/views/setting/index.html.erb b/app/views/setting/index.html.erb
index 78d3487..7d045e3 100644
--- a/app/views/setting/index.html.erb
+++ b/app/views/setting/index.html.erb
@@ -11,6 +11,7 @@
     show medias in list view: <%= check_box_tag('list_view_media_rendering', 'checked', @list_view_media_rendering) %><br/>
     open links in new window: <%= check_box_tag('link_open_new_window', 'checked', @link_open_new_window) %><br/>
     open links via mobile proxy(gwt): <%= check_box_tag('link_type_gwt', 'checked', @link_type == 'gwt') %><br/>
+    use mobile phone's GPS info: <%= check_box_tag('use_gps_info', 'checked', @use_gps_info) %><br/>
     <%= submit_tag 'update' %>
   </p>
 <%- end -%>
diff --git a/config/environment.rb b/config/environment.rb
index f9a38ff..ddded84 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -32,6 +32,7 @@ module F2P
       attr_accessor :service_grouping_threashold
       attr_accessor :link_open_new_window
       attr_accessor :link_type # nil or 'gwt'
+      attr_accessor :use_gps_info
       attr_accessor :updated_expiration
       attr_accessor :list_view_media_rendering
     end
@@ -106,15 +107,15 @@ Rails::Initializer.run do |config|
   F2P::Config.cipher_algorithm = 'AES-256-CBC'
   F2P::Config.cipher_block_size = 16 # must match with above alg.
   F2P::Config.encryption_key = "xxxx"
-  F2P::Config.google_maps_api_key = ''
+  F2P::Config.google_maps_api_key = 'xxxx'
   F2P::Config.icon_url_base = '/images/icons/'

   F2P::Config.google_maps_maptype = 'mobile'
-  F2P::Config.google_maps_zoom = 13
+  F2P::Config.google_maps_zoom = 12
   F2P::Config.google_maps_width = 160
   F2P::Config.google_maps_height = 80

-  F2P::Config.font_size = 9
+  F2P::Config.font_size = 11
   F2P::Config.entries_in_page = 20
   F2P::Config.text_folding_size = 140
   F2P::Config.entries_in_thread = 4
@@ -122,6 +123,7 @@ Rails::Initializer.run do |config|
   F2P::Config.service_grouping_threashold = 5400
   F2P::Config.link_open_new_window = false
   F2P::Config.link_type = 'gwt'
+  F2P::Config.use_gps_info = false
   F2P::Config.updated_expiration = 5400
   F2P::Config.list_view_media_rendering = true
 end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 85c9a60..b18ad27 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -14,4 +14,8 @@ config.action_view.debug_rjs                         = true
 config.action_controller.perform_caching             = false

 # Don't care if the mailer can't send
-config.action_mailer.raise_delivery_errors = false
\ No newline at end of file
+config.action_mailer.raise_delivery_errors = false
+
+# SyslogLogger
+require 'syslog_logger'
+RAILS_DEFAULT_LOGGER = SyslogLogger.new 'f2p'
diff --git a/config/environments/production.rb b/config/environments/production.rb
index ec5b7bc..2542ee7 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -22,3 +22,7 @@ config.action_controller.perform_caching             = true

 # Disable delivery errors, bad email addresses will be ignored
 # config.action_mailer.raise_delivery_errors = false
+
+# SyslogLogger
+require 'syslog_logger'
+RAILS_DEFAULT_LOGGER = SyslogLogger.new 'f2p'
diff --git a/lib/google_maps.rb b/lib/google_maps.rb
index ec6982c..f0c819d 100644
--- a/lib/google_maps.rb
+++ b/lib/google_maps.rb
@@ -72,6 +72,8 @@ module GoogleMaps
   end

   class GoogleGeocoder
+    include HashUtils
+
     URL = 'http://maps.google.com/maps/geo'

     def initialize(httpclient, key)
@@ -79,16 +81,56 @@ module GoogleMaps
       @key = key
     end

-    def search(str, hl = 'ja')
+    def search(str, en = 'utf-8')
       query = {
         'q' => str,
         'output' => 'json',
-        'hl' => hl,
+        'en' => en,
+        'key' => @key
+      }
+      result = JSON.parse(NKF.nkf('-wm0', @httpclient.get_content(URL, query)))
+      if result.nil? or v(result, 'error')
+        nil
+      elsif v(result, 'Status', 'code') != 200
+       nil
+      elsif v(result, 'Placemark').size > 1
+        Candidates.new(v(result, 'Placemark'))
+      else
+        address = v(result, 'name')
+        address = nil if address and address.empty?
+        c = v(result, 'Placemark')
+        cord = v(c[0], 'Point', 'coordinates')
+       lat = cord[1]
+        long = cord[0]
+        if address and lat and long
+          Point.new(address, lat, long)
+        end
+      end
+    end
+
+    def reversesearch(lat, long, oe = 'utf-8')
+      query = {
+        'll' => lat + ',' + long,
+        'output' => 'json',
+        'oe' => oe,
         'key' => @key
       }
       result = JSON.parse(NKF.nkf('-wm0', @httpclient.get_content(URL, query)))
-      # TODO: need to construct Point or Candidates.
-      nil
+      if result.nil? or v(result, 'error')
+        nil
+      elsif v(result, 'Status', 'code') != 200
+       nil
+      else
+        address = v(result, 'name')
+        address = nil if address and address.empty?
+        c = v(result, 'Placemark')
+        cord = v(c[0], 'Point', 'coordinates')
+       lat = cord[1]
+        long = cord[0]
+        if address and lat and long
+          Point.new(address, lat, long)
+        end
+      end
     end
   end

@@ -133,5 +175,7 @@ end
 if $0 == __FILE__
   require 'httpclient'
   require 'pp'
-  pp GoogleMaps::Geocoder.new(HTTPClient).search('日本、東京駅')
+  pp GoogleMaps::GeocodingJpGeocoder.new(HTTPClient).search('日本、東京駅')
+  pp GoogleMaps::GoogleGeocoder.new(HTTPClient).search('日本、東京駅')
+  pp GoogleMaps::GoogleGeocoder.new(HTTPClient).reversesearch(35.306, 139.274)
 end