Sabtu, 26 Oktober 2013

Geocode di Rails


Pada sesi sebelumnya kita telah mempelajari bagaimana cara instalasi Google Maps v3 pada aplikasi Rails yang kita buat dan juga field autocomplete dengan menggunakan jQuery Flexbox

Sesi kali ini akan membahas kasus lain dari google maps dan pointing lokasi berdasarkan input nama daerah atau lebih dikenal dengan nama geocode. Sebagai contoh di sini provinsi dan nama kota berasal dari Indonesia. 
Misalkan kita input Jawa Barat sebagai provinsi dan Bandung sebagai nama kotanya, maka google maps akan pointing ke kota Bandung provinsi Jawa Barat.

Pertama kita buat model user yang berisi nama serta informasi lokasinya


rails g model user name:string city_name:string state_name:string country_name:string


Untuk memuat data lokasi sebagai opsi, buat juga model city dan statenya



rails g model city name:string state_id:integer

rails g model state name:string


dan rake db:migrate

Setelah itu buat relasi antara city dan state 
app/models/city.rb

class City > ActiveRecord::Base
 belongs_to :state
end

app/models/state.rb

class State > ActiveRecord::Base
 has_many :cities
end

Relasi antara state dan city akan memudahkan untuk populate nama kota berdasarkan nama provinsinya masing-masing. Untuk mempermudah dalam membuat data state dan city, tambahkan di app/db/seeds.rb


State.create(
 [
  {:name => "West Java"},
  {:name => "Central Java"},
  {:name => "East Java"}
 ]
)

#City
country = Country.find_by_name("Indonesia")
west_java = State.find_by_name("West Java")
central_java = State.find_by_name("Central Java")
east_java = State.find_by_name("East Java")
City.create(
 [
  {:name => "Bandung", :state_id => "#{west_java.id}"},
  {:name => "Bogor", :state_id => "#{west_java.id}"},
  {:name => "Sukabumi", :state_id => "#{west_java.id}"},
  {:name => "Semarang", :state_id => "#{central_java.id}"},
  {:name => "Kudus", :state_id => "#{central_java.id}"},
  {:name => "Demak", :state_id => "#{central_java.id}"},
  {:name => "Surabaya", :state_id => "#{east_java.id}"},
  {:name => "Lamongan", :state_id => "#{east_java.id}"},
  {:name => "Tulungagung", :state_id => "#{east_java.id}"}
 ]
)
Lalu jalankan di console rake db:seed 


Setelah langkah-langkah di atas baru kita bisa mulai membuat bagian view nya. Misal kita akan membuat user baru beserta data lokasinya. Buat method di app/controllers/users_controller.rb untuk halaman create user.
def new
  @user = User.new
end

private 

def prepare_state
 @states = State.all.map{|x|x.name}.unshift(['Please select', ''])
end
Lalu buat app/views/users/new.html.erb yang berisi form untuk create user : 
<script type="text/javascript"
      src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAVdlyqpDfn1sXmYcY9WJS4cfU0jnkIWao&sensor=false">
    </script>
<script type="text/javascript">
      function initialize() {
        var mapOptions = {
          center: new google.maps.LatLng(-7.2099,110.606918),
          zoom: 3,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        var map = new google.maps.Map(document.getElementById("map-canvas"),
            mapOptions);
      }
      google.maps.event.addDomListener(window, 'load', initialize);
  
  $(document).ready(function(){
    initialize();
  })
</script>

<%= form_for @user do |form| %>
    <%= form.label :name %>&nbsp;<%= form.text_field :name %>
     <br />
      <div class="locations">
      <%= form.label :state_name, "Provinsi" %>&nbsp;<%= form.select :state_name, options_for_select(@states) %>
      <br />
       <%= form.label :city_name, "Kota" %>&nbsp;<div id="city_name"></div>
       <%= form.hidden_field :city_name %>
     </div>
     <br />
   <div id="map-canvas" style="width:300px;height:200px"/>
<%end%>
View di atas masih berupa form create user dan google maps saja. 



Untuk pointing lokasi di map berdasarkan input diperlukan beberapa fungsi javascript untuk trigger ketika dropdown provinsi dipilih. Input nama kota di atas juga menggunakan jquery flexbox untuk autocomplete  yang telah dibahas pada sesi sebelumnya.

Fungsi trigger untuk pointing lokasi di map berdasarkan input provinsi diambil contoh seperti ini
<script type="text/javascript">
$(document).ready(function(){
   $("#user_state_name").on("change", function(){
     set_map($(this).val());
  });


      function set_map(city,state,country){
        var mapOptions = {
          center: new google.maps.LatLng(-7.2099,110.606918),
          zoom: 7,
          mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        country = "Indonesia";
        var map = new google.maps.Map(document.getElementById("map-canvas"),
        mapOptions);

        if(city == ''){
          address = state + ',' + "Indonesia"
        }else{
          address = city + ',' + state + ',' + "Indonesia"
        }

        var geocoder = new google.maps.Geocoder();

        geocoder.geocode({
          'address':address
        },

        function(result, status){
          if (status == google.maps.GeocoderStatus.OK){
            map.setCenter(result[0].geometry.location);

            var marker = new google.maps.Marker({
              map: map,
              position: result[0].geometry.location
            });
          }else{
            alert("Geocode was not successful for the following reason: " + status);
          }
        });
        return false;
      }
</script>
Selain pointing lokasi, ketika dropdown provinsi dipilih juga akan populate nama kota di bawah provinsi tersebut. Agar data provinsi dan kota sesuai maka diperlukan relasi antara keduanya serta AJAX untuk merequest perubahan data city berdasarkan state yang dipilih. 

Pertama kita tambahkan buat method populate_cities di app/controllers/users_controller.rb. Method tersebut hanya untuk load halaman partial untuk meload kembali form user tapi dengan catatan field city yang sudah berisi data city sesuai state yang dipilih.
def populate_cities
  render :layout => false
end

Setelah itu buat fungsi javascript untuk populate_cities ketika dropdown provinsi dipilih
$(document).ready(function(){
   $("#user_state_name").on("change", function(){
    populate_cities();
     set_map($(this).val());

   function populate_cities(){
      var base_address = $(".locations");
      var controller = "<%= populate_cities_users_path %>";
      $.ajax({
        type: "GET",
        url: escape(controller),
        data: {"state_name" : $('#user_state_name').val()},
        dataType:"html",
        beforeSend:function(){
          $('#city_name_input').attr('disabled', 'disabled').css('background-color', '#E6E6E6');
        },
        success:function(data){
          jQuery(".locations").html(data);
        }
      })
    }
  });
Ketika fungsi tersebut di-execute maka akan merender halaman partial tanpa harus meload ulang satu halaman untuk populate data city. Buat halaman partial sesuai nama method populate_cities (app/views/users/populate_cities.erb). Pada halaman partial tersebut juga disertakan fungsi javascript untuk autocomplete nama kotanya. 


<script type="text/javascript">
  function cities_flexbox(){
    var query = "<%= @city_name %>";
    jQuery("#city_name").flexbox('<%= autocomplete_city_users_path %>',{
      customParameters: {q: query, state_name: $('#user_state_name').val()},
      method: 'post',
      watermark: query,
      showArrow: false,
      inputClass: 'inpu',
      minChars: 2,
      width:200,
      queryDelay: 300,
      paging:true,
      highlightMatches:true,
      containerClass:'ffbContainer',
      onSelect: function(){
        $("#user_city_name").attr('value', this.getAttribute('hiddenValue'));
        set_map($("#user_city_name").val(),$("#user_state_name").val(),'Indonesia');
      }
    });
  }

  $(function(){
    cities_flexbox();
  });
</script>
  <label>Provinsi :</label>&nbsp;
  <%= select_tag 'user[state_name]', options_for_select(@states, params[:state_name]) %>
  <br />
  <label>Kota :</label>
<div id="city_name"></div>
<%= hidden_field_tag "user[city_name]" %>
Fungsi cities_flexbox()di atas merupakan fungsi javascript untuk meload field input autocomplete. Ketika field input autocomplete diisi nama city maka akan muncul datanya sesuai dengan keywordnya dan ketika dipilih maka google maps akan pointing lokasi berdasarkan state dan city nya. 

Berikut ini adalah hasil dari geocode yang sudah terintegrasi dengan rails :









Selamat mencoba dan happy coding :)


Tidak ada komentar:

Posting Komentar