Sunday, 22 March 2009

  • Ruby on Rails Geotagging 方案(Ajax+GeoKit+Ym4r)

    Ruby on Rails Geotagging 方案(Ajax+GeoKit+Ym4r)


    GoogleMap 使 Mashup 達到了新的境界,很多網站也新加入了地圖系統。這次看看怎樣使用RoR 的Plugins和Ajax 功能,可以一兩個小時便建立一個Geotagging 系統。


    Step 1: 建立Ajax 功能


    首先,我們建立RoR 必要的Controller 和 Views,並加入Ajax 功能。

    建立map folder和檔案

    可以利用 RoR application 附帶的 script 得到基本的 Controller 和 Views。鍵入以下指令

    ruby script/generate controller map



    RoR 自動建立了相關檔案和Tests

    設定layouts


    一個頁面最重要是結構。RoR 提供的方法使用者可以輕易把結構分享給各個頁面,這便是 layouts 的利害之處,也是RoR 提倡的 DRY (Don't Repeat Yourself)。

    打開 /app/controllers/ 內的 map_controller.rb ,鍵入以下編碼:


    class MapController < ApplicationController
      layout "blogs"

    end

    意思是在這個 map controller 內的所有頁面也是使用"blogs"這個 layout。因此,我們在 /app/views/layouts 內要加入 'blogs.html.erb' 檔案:



    新增 blogs.html.erb layout 檔案

    在這檔案貼上以下編碼:


    <html>
      <head>
        <%= javascript_include_tag :defaults %>
      </head>
    <body>
    <%=yield%>

    </body>
    </html>

    很像普通的 html 吧!重點是以下兩句:

    1. <%=javascript_include_tag :defaults%> - 把預設的javascripts 放到這裡,這包括 RoR 非常喜歡的 prototype framework
    2. <%=yield%> - 把相關頁面的結果在這裡顯示。

    在 index 頁面加入 form

    現在處理主要的頁面了。在/app/views/map 內新增 index.html.erb



    index 是預設頁面

    'index.html.erb' 像我們平日使用 index.html 一樣,成為 map controller 的預設頁面。而 .erb 代表它是 ruby files.

    把下面編碼貼在index.html.erb 內

      <% form_remote_tag :url => '/map/location_search' do -%>
        <label>Location:</label> <%= text_field_tag 'location' %></br>
        <div><%= submit_tag 'Go' %></div>
      <% end -%>

      <div id="results"></div>

    這裡使用了幾個常見的helper methods

    1. form_remote_tag: 使這張form 變為Ajax form。重點是簡單的"remote",代表了Ajax的意思。而:url 定義伺服器的目標 action (/map/location_search)。
    2. text_field_tag: 文字輸入鍵,它的 id 和 name 值設定為'location'
    3. submit_tag: 這張form 的 submit 鍵,其值為'Go'
    '<div id="results"></div>' 留待我們接收伺服器的結果。


    是時間測試了!重啟server,在你的<application root> 鍵入

    ruby script/server

    這是在瀏覽器鍵入 http://localhost:3000/map 便可看到結果!



    瀏覽器看到的結果




    Ajax 效果

    因為在form_remote_tag 定義了伺服器的目標為 '/map/location_search'。所以我們便要建立相關的編碼, 在 /app/views/map 內新增一個'location_search.rjs' 檔案




    建立rjs 檔案

    .rjs 檔案是RoR重要的Ajax 工具。在這檔案內把我們所要的javascripts 包裝得像普通的 ruby 編碼,而且簡單得不能再簡單。把以下編碼加入到location_search.rjs


    page.replace_html 'results', "#{params[:location] } is requested at #{Time.now}"

    page.replace_htm 把 id 為 'results' 的 innerHTML 物件修改。"#{...} something" 是 Ruby 的String 物件,#{}可以放入其他Variable。而RoR 會把所有html Request parameters 放到params變數內。

    所以,這時在剛才的form 鍵入'Hong Kong',並按Go 鍵,便可看到以下結果:


    Ajax 效果



    Step 2: 安裝Geokit 找出地理位置



    RubyGems 幫助我們省卻很多編碼,這次我們找到了很好用的地圖工具 geokit Rubygems (http://geokit.rubyforge.com)

    安裝geokit

    安裝相關的gems,先確保Rubygems 記錄了下載的網址:

    gem sources -a http://gems.github.com

    可以安裝 geokit gems了:

    gem install andre-geokit

    使用GoogleMaps API,必需要先登記一個 Key,可從下面網站登記:

    http://code.google.com/intl/zh-TW/apis/maps/signup.html

    現在我們用 'localhost' 測試,在 API 申請表上的 'My Web Site URL' 填上 'http://localhost' 便可。我們便得到一條像下面字句的 Key:

    ABQIAAAA54q9My7rL_Hcn6q9UGHTUxT2yXp_ZAY8_ufC3CFXhHIE1NvwkxRroKVvZLA8rVtuMyXjGkh0c8tDeg
    使用這 Key, 把以下編碼放到 <application root>/app/config/environment.rb 最底部份 ( 別放在 Rails::Initializer.run do |config| .... end 那個 loop 內!):


    require 'geokit'

    GeoKit::Geocoders::google = 'YOUR_KEY_HERE'

    GeoKit::Geocoders::provider_order = [:google]


    設定完成,可以使用Geokit 了!打開 <application root>/app/controllers/map_controller.rb,貼上以下編碼:

    class MapController < ApplicationController
      layout "blogs"
     
      def location_search
        @loc = GeoKit::Geocoders::MultiGeocoder.geocode(params[:location] )

      end

    end


    然後,打開<application root>/app/views/location_search.rjs,貼上下面編碼:

    page.replace_html 'results', "#{ } located <b>#{@loc.lat}, #{@loc.lng}</b>"


    剛才在 Controller 的相關 action 中 (location_search),使用了Geokit 找出輸入地點的經緯度,把結果記錄在 @loc 這變數內。所以在 location_search.rjs 這個 View 可以直接使用這變數。這得益於RoR 的MVC 結構,把編碼變得清晰易明。


    現在可以測試了!如果未打開Webrick,在 <application root> 輸入以下指令:

    ruby script/server



    輸入 Tokyo, 並按 Go 鍵,可以得出以上結果!



    Step 3: 用 ym4r 在網頁顯示 Google Map 地圖

    RoR projects 可以安裝Plugins 以增加功能,相似於在Firefox 安裝 Add-ons 一樣。Plugins 和 Rubygems 概括的分別在於前者是給某一個project 使用,後者是給整個ruby 系統使用。使用時分別不大。


    安裝plugins

    首先,我們需要 svn。如果沒有的話,便要先安裝了。請先到 http://subversion.tigris.org/getting.html 看看如何安裝。如果是 ubuntu 的話,鍵入以下指令便可:

    apt-get install subversion

    以上指令只須執行一次,然後便可安裝plugins 。在<application root folder> 輸入:

    ruby script/plugin install svn://rubyforge.org/var/svn/ym4r/Plugins/GM/trunk/ym4r_gm
    這時<application root folder>/vendor/plugins 會多了一個 ym4r folder。


    ruby script/plugin 指令下載了整個 ym4r plugins

    在開始使用 ym4r 前,先設定Google Map Api Key 給 ym4r。ym4r 提供了一個便捷的方法設定此 Key 給不同的環境。打開<application root folder>/config/gmaps_api_key.yml,貼上:

    development:
      YOUR_KEY_HERE

    test:
      YOUR_KEY_HERE

    production:
      YOUR_KEY_HERE



    把剛才為 geokit 登記的 Google Map API Key 貼在上面YOUR_KEY_HERE 位置。安裝完成!

    修改 MVC

    現在可以使用Plugins 的功能了。因為所有邏輯應放在 Controller 內,所以打開 Map Controller (<application root folder>/app/controllers/map_controller.rb),貼上下面的編碼。


    class MapController < ApplicationController

      # default action of the controller
      def index
        @map = GMap.new('map_div')
        @map.control_init(:large_map => true, :map_type => true)
        @map.center_zoom_init([75.5, -42.56], 4)
        @map.overlay_init(GMarker.new([75.6, -42.467], :title=> "Hello World", :info_window => "I am Here"))
      end

      def location_search
        @loc = GeoKit::Geocoders::MultiGeocoder.geocode(params[:location])
      end

    end


    上面編碼新增了 index action,@map 在這裡定義。對於有使用Google Map 編碼的朋友,那些 @map functions 似曾相識。不錯,ym4r 主要是把Google Map Api 的功能 Railize!在 index 的頁面便可使用Google Map 這物件了。index 內的四行便建立了 GMap 物件並存於@map 中,加上了 control ,把 Map的中心設定,還加入了一個 Marker!是否十分易懂?

    要使用 Google Map ,首先要把其 javascript library 放進頁面。RoR 的結構和 ym4r 令這一步驟十分輕鬆。

    把以下編碼貼在 <application root folder>/app/views/layouts/blog.html.erb 內:


    <html>
    <head>
       <%= javascript_include_tag :defaults %>
       <%= GMap.header %>
       <%= @map.to_html %>
    </head>
    <body>
    <%=yield%>
    </body>
    </html>

    新增了 GMap.header @map.to_html 兩句,便把需要的 javascript 放到頁面內。

    我們可以在 index 頁面使用 Google Map 了,把下面編碼貼在 <application root folder>/app/views/map/index.html.erb




      <% form_remote_tag :url => '/map/location_search' do -%>
        <label>Location:</label> <%= text_field_tag 'location' %></br>
        <div><%= submit_tag 'Go' %></div>
      <% end -%>

      <div id="results"></div>
     

      <%= @map.div(:width => 600, :height => 400) %>


    以上編碼新增了 <%= @map.div(:width => 600, :height => 400) %> 一句,便把所要的 Map HTML 放到頁面,並設定了它的長和濶。

    這時我們可以試試結果。不過,安裝 Plugin 之後,也要重啟 application 的。先停止現在執行的 application,並再次輸入:

    ruby script/server


    在瀏覽器可以看到

     
    剛才設定的 Google Map 在這裡顯示出來!

    成功,看到Google Map 了!

    (如果你看到 undefined method 'relative_url_root' for ActionContoller::AbstractRequest:Class 這錯誤,請到這裡看看 http://railsforum.com/viewtopic.php?id=24839 。修改map.rb 後應該成功)


    Step 4: Mashup: 用 geokit 和 ym4r Geotagging


    geokit 和 ym4r 強勁吧!結合兩者會有什麼結果!?

    先把 <application root folder>/app/controllers/map_controller.rb 的 location_search 換成以下編碼:

      def location_search
        @loc = GeoKit::Geocoders::MultiGeocoder.geocode(params[:location] )
        @map = Variable.new("map")
        @marker = GMarker.new([@loc.lat,@loc.lng], :title => params[:location], :info_window => "Marker for #{params[:location]}")

      end

    @map = Variable.new("map") 中的@map 聯絡了剛才的 GMap 物件,即是我們剛看見在頁面上的Google Map 。再GMarker 便把Geokit 找到的 geocode 資料建立一個 Marker 物件。


    我們最後要修改 Ajax 的結果了,在 <application root folder>/app/views/map/location_search.rjs 最底部份貼上:


    page << @map.clear_overlays
    page << @map.add_overlay(@marker)
    page << @map.center_and_zoom_on_markers([@marker])


    上面編碼先取消地圖上的Overlays ,包括所有存在的 Markers,然後加入新的一個 Marker,最後置中於新 Marker 的位置。


    完成!


    打入 Tokyo 並按Ok 鍵便會找到東京的位置!



    總結

    這樣的 geotagging mashup 主要由幾個步驟完成:

    1. 在 RoR 建立Ajax function,主要是設定 layout ,包括建立 ajax form 的頁面和 .rjs server side 檔案。
    2. 安裝 geokit 找出輸入的地理位置。
    3. 安裝 ym4r plugin ,設定和建立頁面上的 Google Map。
    4. Mashup 以上項目。
    RoR 有很多有用的gems 和 plugins,可以發揮你的想像力,Mashup 各樣東西。你可否把 flickr 的相片加到 Google Map 內呢?


    此文章的原始檔可在 http://github.com/arthurccube/rails_examples_ajax_geokit_ym4r/tree/master 下載
  • Choose Identity

  • Give eProps (?)

  • New! You can now edit your comments for 15 minutes after submitting.

Who recommended?