Tìm vị trí với gem Geocoder
Bài đăng này đã không được cập nhật trong 3 năm
1, Giới thiệu
Trong một dự án tôi đã từng làm có một bài toán liên quan tới tìm vị trí như sau:
Người dùng nhập mã bưu điện postal code, hệ thống sẽ tự động tìm ra thông tin vị trí tương ứng với postal code mà người dùng đã nhập như: tên quốc gia, tên tỉnh thành, quận huyện, xã phường. Thoạt đầu tôi có một chút lo lắng liệu không biết có một thư viện nào hỗ trợ điều này không, nó có đủ lớn để tìm vị trí trên nhiều nước khác nhau không, thông tin nó trả về có đầy đủ không. Trường hợp xấu nhất theo tôi là phải dùng Google map api để get thông tin vị trí, nhưng như thế hơi thủ công và mất nhiều thời gian. Thật may cuối cùng tôi đã tìm được gem Geocoder nó đáp ứng đầy đủ các yêu cầu của bài toán tôi đang gặp phải.
Trong bài viết này tôi xin giới thiệu với các bạn cách dùng gem Geocoder để tìm thông tin vị trí thông qua mã bưu điện postal code giống như bài toán tôi gặp phải.
2, Cách sử dụng gem Geocoder giải quyết bài toán tìm vị trí qua postal code
2.1, Cài đặt
a, Cài gem Geocoder
Trong file Gemfile thêm dòng sau:
gem 'geocoder'
Và chạy lệnh
bundle install
b, Thêm thư viện AngularJs
Tôi sử dụng thư viện AngularJs để xử lý các logic trên client, cũng như là dùng ajax để thực hiện request tới server khi người dùng thao tác với hệ thống
-
Bạn download file angular.min.js từ url: https://code.angularjs.org/1.5.6/angular.min.js sau đó thêm nó tới thư mục app/assets/javascripts/lib
-
Để sử dụng thư viện này bạn cần require nó trong file application.js như sau:
//= require angular.min
2.2, Thêm view và tạo ajax request
Bạn tạo file pages/index.html.erb tương ứng với index action của pages controller
File có nội dung như sau:
<div class="container" ng-app="geocoderApp" ng-controller="addressController as vm">
<div class="page-header">
<h3>Geocoder Search With Postal Code</h3>
</div>
<div class="row">
<div class="col-md-6">
<%= text_field_tag :postal_code, nil, placeholder: "Enter postal code",
class: "form-control", "ng-change": "vm.changePostalCode()", "ng-model": "vm.postalCode" %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= text_field_tag :country, nil, placeholder: "Country Name",
readonly: true, class: "form-control", "ng-model": "vm.addressInfo.country" %>
</div>
<div class="col-md-6">
<%= text_field_tag :area, nil, placeholder: "Area Name",
readonly: true, class: "form-control", "ng-model": "vm.addressInfo.area" %>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= text_field_tag :address, nil, placeholder: "Address", readonly: true,
class: "form-control", "ng-model": "vm.addressInfo.address" %>
</div>
</div>
</div>
Kết quả như hình bên dưới:
Giao diện rất đơn giản chỉ gồm 4 textfiles, textfile đầu tiên để người dùng nhập postal code
Trong file view này, tôi cũng đã định nghĩa angular app và controller, cùng các models và event để đảm bảo ứng dụng bắt được sự kiện người dùng nhập postal code và gửi postal code đó tới server để lấy data về.
Sau đó chúng ta thêm file assets/javascripts/angular/apps/geocoder_app.js, để định nghĩa angular app, file có nội dung như sau:
angular.module('geocoderApp', []);
Tiếp theo, chúng ta thêm file assets/javascripts/angular/controllers/address_controller.js để tạo một angular controller, file có nội dung như sau:
'use strict';
angular.module('geocoderApp').controller('addressController', addressController);
addressController.$inject = ['$http'];
function addressController($http) {
var vm = this;
vm.addressInfo = {country: null, area: null, address: null};
vm.changePostalCode = function() {
var promise = $http.get('/address_info', {postal_code: vm.postalCode});
promise.success(function(data){
vm.addressInfo = data;
});
};
}
Trong nội dung của controller này, chúng ta có thể thấy phần gọi ajax request để lấy thông tin address. Để gọi một ajax request trong angular, tôi sử dụng $http service
2.3, Xử lý dữ liệu trên server
Ở phần trên, chúng ta đã thực hiện gửi một ajax tới server để yêu cầu có thông tin về address tương ứng với postal code chúng ta đã nhập
Tiếp theo, chúng ta sẽ thêm code để server lấy postal code từ client gửi lên và dùng thư viện geocoder để lấy về dữ liệu tương ứng
Thêm dòng sau tới routes.rb file để định nghĩa controller
get "/address_info" => "addresses#index"
Thêm dòng sau tới application.rb
config.autoload_paths += %W(#{config.root}/lib)
Thêm file lib/myapp/geocoder.rb có nội dung như sau:
class Myapp::Geocoder
TYPES = {
country: "country",
area: "administrative_area_level_1",
address_1_part_1: %w(locality political),
address_1_part_2: "sublocality_level_1"
}
def initialize search_term, language
@search_term = search_term
@language = language
@address_info = search_address
end
def full_address_info_hash
{
country: get_location,
area: get_area,
address: get_first_address
}
end
private
attr_reader :search_term, :language, :address_info
def search_address
address = Geocoder.search search_term, params: {language: language}
return {} unless address.present?
address.first.data["address_components"]
end
def choose_address_item_by type
address_info.find do |entry|
type.is_a?(Array) ? entry["types"] == type : type.in?(entry["types"])
end.try(:[], "long_name").to_s
end
def get_location
choose_address_item_by TYPES[:country]
end
def get_area
choose_address_item_by TYPES[:area]
end
def get_first_address
separate_char = language == :ja ? "" : " "
[choose_address_item_by(TYPES[:address_1_part_1]),
choose_address_item_by(TYPES[:address_1_part_2])].join separate_char
end
end
Thêm file app/controllers/addresses_controller.rb có nội dung như sau:
class AddressesController < ApplicationController
def index
render json: Myapp::Geocoder.new(params[:postal_code], "en").full_address_info_hash
end
end
Đến đây thì ứng dụng của chúng ta đã hoàn thành, bạn có thể chạy ứng dụng và xem kết quả
3, Kết luận
Geocoder gem thực sự là một gem hỗ trợ cực tốt cho việc tìm các thông tin addess theo nhiều thể loại thông tin đầu vào. Ở bài viết này tôi mới chỉ áp dụng nó vào việc tìm kiếm address information theo postal code, geocoder còn rất nhiều tính năng hữu dụng khác, bạn có thể tham khảo tại: https://github.com/alexreisner/geocoder
Cảm ơn các bạn đã quan tâm đến bài viết của tôi, hẹn gặp lại ở các bài viết tiếp theo.
</br>
Tài liệu tham khảo:
https://github.com/alexreisner/geocoder
http://railscasts.com/episodes/273-geocoder?view=asciicast
All rights reserved