Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

enable integration with geocoder for mongo using reverse internal array format [lng,lat] #263

Closed
wants to merge 6 commits into from

4 participants

@kristianmandrup

Essential improvement for mongo geocoder integration! as requested in an issue... ;)

@apneadiving

I appreciate your help on the gem but I don't understand why you make different modifications in one pull request.

Actually, your modifications on lib/gmaps4rails/helper/gmaps4rails_helper.rb seem to make it unusable (as far as I've read it, it's buggy) and I don't understand why you need this kind of functionnality.

I was playing around a bit. Just some ideas. I felt the partials were much more natural as simple view helpers (i.e avoid complex decision logic such as case + if/else in a view).

I had a use case where I needed the google map apis loaded for my gmaps-autocomplete gem. I felt it was easier to reuse/leverage the existing functionality you had created to load scripts for google apis, but I needed it without rendering the map.
I'm aware it was far from being an optimal pull request.

@kristianmandrup

Some experimentations for loading options from a yaml file. Not yet tested

@nengine

I have the same problem when use with Geocoder. Array for lng, lat is reversed so map is not displaying properly. How can I fix this? thanks

@kristianmandrup

Look at my fork and latest pull requests ;)

@nengine

Thanks @kristianmandrup, Please let me know anyway that I could do that functionality by overwriting methods + options in the code. I do not know how to make changes in the gem! Thanks a alot.

@nengine

In the original gem, I notice :lat_column => args[:lat] || "latitude", :lng_column => args[:lng] || "longitude", options. Is that something I could use to integrate with geocoder?

@nengine

I made the changes and passed the options but still getting in reverse order. In this case, lat is 40.7519846 and lng is -73.9697795 and this is how it is stored in location field. But when I do User.all.to_gmaps4rails they are still coming in reverse order.

  acts_as_gmappable :lat_lng_array => :location, :process_geocoding => false , :pos_order => [:lat, :lng ]
  field :location, :type => Array
  field :gmaps, :type => Boolean

  geocoded_by :address , :coordinates => :location
  after_validation :perform_geocoding

u.location
[-73.9697795, 40.7519846]

User.all.to_gmaps4rails
"[{\"lat\":-73.9697795,\"lng\":40.7519846},{\"lat\":-73.8477874,\"lng\":40.7209975}]"

@kristianmandrup

If you use my fork you should be able to run a spec that at least demonstrates that the Array can be stored in either: [lat,lng] or [lng, lat] format. Not sure about the to_gmaps4rails method, but should be easy to use the same config option to customize that functionality as well to return in the format you want.

@nengine

Thanks for your reply. now I can call User.all.to_gmaps4rails and getting lat, lng in the order I wanted. But getting error when it is rendering the view.

NoMethodError in Home#index

Showing C:/Users/nash/crisis/app/views/home/index.html.erb where line #4 raised:

undefined method `config' for Gmaps4rails:Module
Extracted source (around line #4):

1:

Home#index


2:

Find me in app/views/home/index.html.erb


3:
4: <%= gmaps4rails(@json) %>

@hollanddd

should be <%= gmaps(@json) %> I believe.

@kristianmandrup

@neuralnw did you make a pull request with the changes to master? Would also be nice with a full example in the Wiki demonstrating how to configure this I think.

@apneadiving
Owner

Sorry guys I've completely changed the gem in v2.

No more geocoding!

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
8 Gemfile.lock
@@ -91,7 +91,14 @@ GEM
treetop (~> 1.4.8)
method_source (0.7.1)
mime-types (1.18)
+ mongoid (3.0.5)
+ activemodel (~> 3.1)
+ moped (~> 1.1)
+ origin (~> 1.0)
+ tzinfo (~> 0.3.22)
+ moped (1.2.1)
multi_json (1.2.0)
+ origin (1.0.7)
polyglot (0.3.3)
pry (0.9.8.4)
coderay (~> 1.0.5)
@@ -172,6 +179,7 @@ DEPENDENCIES
guard-spork
jasmine
jquery-rails
+ mongoid (~> 3)
pry
rails (~> 3.2.1)
rspec-rails
View
6 README.rdoc
@@ -74,6 +74,12 @@ Mongoid example:
field :location, :type => Array
+By default this uses the [lng, lat] position format. You can reverse this format, by specifying `:pos_order => [:lng, :lat]`
+
+From [geocoder](https://github.com/alexreisner/geocoder) - Latitude/Longitude Order.
+
+_"Coordinates are generally printed and spoken as latitude, then longitude ([lat,lon]).Geocoder respects this convention and always expects method arguments to be given in [lat,lon] order. However, MongoDB requires that coordinates be stored in [lon,lat] order as per the GeoJSON spec (geojson.org/geojson-spec.html#positions), so internally they are stored “backwards.” However, this does not affect order of arguments to methods when using Mongoid or MongoMapper."_
+
== How to?
=== QuickStart!
In your controller:
View
20 app/views/gmaps4rails/_gmaps4rails.html.erb
@@ -1,20 +0,0 @@
-<% content_for :scripts do %>
-<%= javascript_include_tag *js_dependencies %>
-
-<script type="text/javascript">
-<%=raw options.to_gmaps4rails %>
-</script>
-<% end %>
-
-<% case dom.map_provider %>
-<% when "mapquest" %>
-<div id="<%= dom.map_id %>" style="width:750px; height:475px;">
- <!-- TODO: change this style dynamically -->
-</div>
-<% when "bing" %>
- <div id="<%= dom.map_id %>" class="<%= dom.map_class %>"></div>
-<% else %>
-<div class="<%= dom.container_class %>">
- <div id="<%= dom.map_id %>" class="<%= dom.map_class %>"></div>
-</div>
-<% end %>
View
3  app/views/gmaps4rails/_gmaps4rails_libs.html.erb
@@ -0,0 +1,3 @@
+<% content_for :scripts do %>
+ <%= javascript_include_tag *js_dependencies %>
+<% end %>
View
2  app/views/gmaps4rails/_gmaps4rails_libs.html.haml
@@ -0,0 +1,2 @@
+- content_for :scripts do
+ = javascript_include_tag *js_dependencies
View
26 lib/generators/gmaps4rails/install_generator.rb
@@ -5,6 +5,8 @@ class InstallGenerator < Rails::Generators::Base
desc 'Creates a Gmaps4rails initializer and copies the assets to the public folder.'
+ class_option :views, :type => :string, :default => nil, :desc => 'copy partials to app/views of Rails app'
+
def copy_locale
if Rails::VERSION::MINOR >= 1
copy_file "#{source_assets_base_path}gmaps4rails.base.js.coffee", "#{destination_assets_base_path}gmaps4rails.base.js.coffee"
@@ -23,6 +25,30 @@ def copy_locale
copy_file "../../../public/stylesheets/gmaps4rails.css", "public/stylesheets/gmaps4rails.css"
end
end
+
+ def copy_views
+ return unless valid_template_type?
+ copy_file File.join(source_views_path, "_gmaps4rails_libs.html.#{template_type}"), File.join(destination_views_path, "_gmaps4rails_lib.html.#{template_type}")
+ end
+
+ protected
+
+ def template_type
+ options[:views].to_s.to_sym
+ end
+
+ def valid_template_type?
+ [:erb, :haml].include? template_type
+ end
+
+ def source_views_path
+ '../../../app/views/gmaps4rails'
+ end
+
+ def destination_views_path
+ 'app/views/gmaps4rails'
+ end
+
def source_assets_base_path
'../../../app/assets/javascripts/gmaps4rails/'
View
1  lib/gmaps4rails.rb
@@ -3,7 +3,6 @@ module Gmaps4rails
require 'gmaps4rails/base'
class Engine < Rails::Engine
-
initializer "gmaps4rails view helpers" do |app|
ActionView::Base.send :include, Gmaps4railsHelper
end
View
3  lib/gmaps4rails/acts_as_gmappable.rb
@@ -33,7 +33,8 @@ def acts_as_gmappable args = {}
# purposefully no default.
# Leaving out the :position arg means we are using the default lat/lng to store coordinates
- :position => args[:position],
+ :position => args[:position],
+ :pos_order => args[:pos_order] || [:lat, :lng],
:msg => args[:msg] || "Address invalid",
:validation => args[:validation].nil? ? true : args[:validation],
View
1  lib/gmaps4rails/base.rb
@@ -8,6 +8,7 @@ module Gmaps4rails
require 'gmaps4rails/extensions/enumerable'
require 'gmaps4rails/extensions/hash'
+ autoload :Config, 'gmaps4rails/config'
autoload :ModelHandler, 'gmaps4rails/model_handler'
autoload :ActsAsGmappable, 'gmaps4rails/acts_as_gmappable'
View
47 lib/gmaps4rails/config.rb
@@ -0,0 +1,47 @@
+require 'singleton'
+
+module Gmaps4rails
+ def self.config
+ Config.instance
+ end
+
+ class Config
+ include Singleton
+
+ def options
+ yaml_loader.load
+ end
+
+ protected
+
+ def yaml_loader
+ @yaml_loader ||= YamlLoader.new
+ end
+
+ class YamlLoader
+ attr_writer :config_file, :config_path
+
+ def load
+ @load ||= HashWithIndifferentAccess.new load_yaml
+ end
+
+ def config_path
+ Rails.root.join 'config', config_file
+ end
+
+ def config_file
+ @config_file ||= 'gmaps4rails.yml'
+ end
+
+ protected
+
+ def load_yaml
+ @yaml ||= begin
+ YAML.load File.read(config_path)
+ rescue
+ {}
+ end
+ end
+ end
+ end
+end
View
66 lib/gmaps4rails/helper/gmaps4rails_helper.rb
@@ -12,22 +12,62 @@ def gmaps4rails(builder)
# full helper to pass all variables and their options
# @params [Hash] options is a Hash containing data and options. Example: { markers:{ data: @json, options: { do_clustering: true } } }
- def gmaps(options)
+ def gmaps(options = {})
+ options = Gmaps4rails.config.options.merge(options)
+
+ gmaps_libs(options) if options[:with_libs]
+
+ options_with_indifferent_access = options.with_indifferent_access
+ view_helper = Gmaps4rails::ViewHelper.new(options_with_indifferent_access)
+ gmaps4rails_map view_helper.dom_attributes, options_with_indifferent_access
+ end
+
+ def gmaps_libs(options = {})
+ options_with_indifferent_access = options.with_indifferent_access
+ view_helper = Gmaps4rails::ViewHelper.new(options_with_indifferent_access)
+
+ js_dependencies = Gmaps4rails.escape_js_url ? view_helper.js_dependencies_array : view_helper.js_dependencies_array.map(&:html_safe)
+
+ content_for :scripts do
+ javascript_include_tag(*js_dependencies)
+ end
+
+ # render partial: '/gmaps4rails/gmaps4rails_libs',
+ # :locals => {
+ # :js_dependencies => js_dependencies,
+ # :options => options_with_indifferent_access
+ # }
+ end
+
+ def gmaps_libs_now(options = {})
options_with_indifferent_access = options.with_indifferent_access
view_helper = Gmaps4rails::ViewHelper.new(options_with_indifferent_access)
- js_dependencies = if Gmaps4rails.escape_js_url
- view_helper.js_dependencies_array
- else
- view_helper.js_dependencies_array.map(&:html_safe)
- end
-
- render :partial => '/gmaps4rails/gmaps4rails',
- :locals => {
- :options => options_with_indifferent_access,
- :js_dependencies => js_dependencies,
- :dom => view_helper.dom_attributes
- }
+ js_dependencies = Gmaps4rails.escape_js_url ? view_helper.js_dependencies_array : view_helper.js_dependencies_array.map(&:html_safe)
+
+ javascript_include_tag(*js_dependencies)
end
+ protected
+
+ def gmaps4rails_map dom, options = {}
+ options.reverse_merge! :style => "width:750px; height:475px;"
+
+ map_script << content_tag(:script, :type => "text/javascript") do
+ raw(options.to_gmaps4rails).html_safe
+ end
+
+ map = case dom.map_provider.to_sym
+ when :mapquest
+ content_tag :div, nil, :id => dom.map_id, :style => options[:style]
+ when :bing
+ content_tag :div, nil, :id => dom.map_id, :class => dom.map_class
+ else
+ content_tag :div, :class => dom.container_class do
+ content_tag :div, nil, :id => dom.map_id, :class => dom.map_class
+ end
+ end
+
+ (map_script + map).html_safe
+ end
end
View
3  lib/gmaps4rails/js_builder.rb
@@ -21,6 +21,7 @@ def initialize(option_hash)
end
def create_js
+ # @js << "$(function() {"
@js << "#{gmap_id} = new #{ map_constructor };"
@js << "Gmaps.#{js_function_name} = function() {"
@@ -34,7 +35,7 @@ def create_js
@js << "#{gmap_id}.callback();"
@js << "};"
@js << "Gmaps.oldOnload = window.onload;\n window.onload = function() { Gmaps.triggerOldOnload(); Gmaps.loadMaps(); };" if load_map?
-
+ # @js << "}); // ready"
@js * ("\n")
end
View
14 lib/gmaps4rails/json_builder.rb
@@ -26,7 +26,7 @@ module Gmaps4rails
#
class JsonBuilder
- delegate :position, :lat_column, :lng_column, :to => :@options
+ delegate :position, :lat_column, :lng_column, :pos_order, :to => :@options
def initialize(object)
@object, @json_hash, @custom_json = object, Hash.new, nil
@@ -127,12 +127,20 @@ def position_from_array?
position #if gmaps4rails_options[:position] is filled, means user is indicating an array
end
+ def lat_index
+ pos_order.first == :lat ? 0 : 1
+ end
+
+ def lng_index
+ pos_order.first == :lng ? 0 : 1
+ end
+
def lat
- position_from_array? ? @object.send("#{position}")[0] : @object.send("#{lat_column}")
+ position_from_array? ? @object.send("#{position}")[lat_index] : @object.send("#{lat_column}")
end
def lng
- position_from_array? ? @object.send("#{position}")[1] : @object.send("#{lng_column}")
+ position_from_array? ? @object.send("#{position}")[lng_index] : @object.send("#{lng_column}")
end
end
View
8 lib/gmaps4rails/model_handler.rb
@@ -4,7 +4,7 @@ class ModelHandler
attr_accessor :options, :object
- delegate :process_geocoding, :check_process, :checker, :lat_column, :lng_column, :position, :msg, :validation,
+ delegate :process_geocoding, :check_process, :checker, :lat_column, :lng_column, :position, :pos_order, :msg, :validation,
:language, :protocol, :address, :callback, :normalized_address,
:to => :options
@@ -31,7 +31,11 @@ def retrieve_coordinates
# sets array for non relationnal db
def set_position
- object.send("#{position}=", [lat, lng])
+ if pos_order.first == :lat
+ object.send("#{position}=", [lat, lng])
+ else
+ object.send("#{position}=", [lng, lat])
+ end
end
#sets regular columns
View
18 spec/dummy/app/models/geo_place.rb
@@ -0,0 +1,18 @@
+if RUBY_VERSION == "1.9.3"
+
+ require 'mongoid'
+
+ class GeoPlace
+ include Mongoid::Document
+ include Gmaps4rails::ActsAsGmappable
+
+ # Array position format in the form: [lng, lat]
+ # This format is f.ex used by *geocoder* gem
+ acts_as_gmappable :address => :address, :position => :pos, :pos_order => [:lng, :lat]
+
+ field :pos, :type => Array
+ field :address, :type => String
+ field :gmaps, :type => Boolean
+ end
+
+end
View
6 spec/dummy/config/gmaps4rails.yml
@@ -0,0 +1,6 @@
+map_options:
+ provider: mapquest
+ provider_key: 12345
+ auto_adjust: true
+ center_on_user: true
+ zoom: 5
View
16 spec/factories/geo_place_factory.rb
@@ -0,0 +1,16 @@
+FactoryGirl.define do
+ factory :geo_place do
+
+ trait :paris do
+ address "Paris, France"
+ end
+
+ trait :invalid do
+ address "home"
+ end
+
+ address "Toulon, France"
+
+ factory :geo_place_paris, :traits => [:paris]
+ end
+end
View
55 spec/lib/config_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+
+class Configuration
+ include Gmaps4railsHelper
+end
+
+describe Gmaps4rails::Config::YamlLoader do
+ subject { Gmaps4rails::Config::YamlLoader.new }
+
+ it 'should load a Hash from config file' do
+ subject.load.should be_a Hash
+ end
+
+ it 'should have a config_file' do
+ subject.config_file.should == 'gmaps4rails.yml'
+ end
+
+ it 'should have a config_path' do
+ subject.config_path.to_s.should include('spec/dummy/config/gmaps4rails.yml')
+ end
+
+ describe '#config_file = ' do
+ let(:config_file) { 'gmaps.yml' }
+
+ before :each do
+ subject.config_file = config_file
+ end
+
+ it 'should set it' do
+ subject.config_file.should == config_file
+ end
+
+ it 'should load an empty Hash if no matching config file' do
+ subject.load.should == {}
+ end
+ end
+end
+
+describe Gmaps4rails::Config do
+ subject { Gmaps4rails.config }
+
+ it 'should have a yaml loader' do
+ subject.send(:yaml_loader).should be_a Gmaps4rails::Config::YamlLoader
+ end
+
+ it 'should load options from yaml file' do
+ subject.options.should be_a Hash
+ subject.options.should_not be_empty
+ end
+
+ it 'should load provider map_options from yaml file' do
+ subject.options[:map_options][:provider].should == 'mapquest'
+ end
+end
View
37 spec/models/geo_place_spec.rb
@@ -0,0 +1,37 @@
+if RUBY_VERSION == "1.9.3"
+
+ require 'spec_helper'
+
+ include Geocoding
+
+ set_gmaps4rails_options!
+
+ # Mongoid 3.x only
+ require 'mongoid'
+ require 'moped'
+
+ Mongoid.configure do |config|
+ config.connect_to('mongoid_geo_test')
+ end
+
+ describe Gmaps4rails::ActsAsGmappable do
+
+ # Tests Array position format in the form: [lng, lat]
+ # F.ex required for integration with *geocoder* gem
+ let(:geo_place) { Factory(:geo_place) }
+ let(:invalid_geo_place) { Factory.build(:invalid_geo_place) }
+
+ before(:each) do
+ Geocoding.stub_geocoding
+ end
+
+ context "standard configuration, valid place" do
+ it "should save longitude and latitude to the customized position array" do
+ set_gmaps4rails_options!(:position => 'location', :pos_order => [:lng, :lat])
+ geo_place.pos.should_not be_nil
+ geo_place.should have_same_position_as TOULON
+ end
+ end
+ end
+
+end
View
14 spec/support/matchers.rb
@@ -30,7 +30,7 @@ def has_same_content_as?(actual, expected)
class PositionMatcher
attr_reader :object, :position_hash
- delegate :position, :lat_column, :lng_column, :to => :@options
+ delegate :position, :lat_column, :lng_column, :pos_order, :to => :@options
def initialize object, position_hash
@object, @position_hash = object, position_hash
@@ -43,12 +43,20 @@ def same_pos?
protected
+ def lat_index
+ pos_order.first == :lat ? 0 : 1
+ end
+
+ def lng_index
+ pos_order.first == :lng ? 0 : 1
+ end
+
def lat
- position ? object.send("#{position}")[0] : object.send("#{lat_column}")
+ position ? object.send("#{position}")[lat_index] : object.send("#{lat_column}")
end
def lng
- position ? object.send("#{position}")[1] : object.send("#{lng_column}")
+ position ? object.send("#{position}")[lng_index] : object.send("#{lng_column}")
end
end
Something went wrong with that request. Please try again.