Iterate through array of arrays and pass coordinates to google map - ruby-on-rails

In my Rails app I have google map that I want to display pins based on coordinates (latitude, longitude). I have dummy data in user.rb file as an array and I'm trying to map through it and pass coordinates to google map, however I'm missing something basic, because all works if I supply it manually. So how do I iterate so coordinates would be displayed on map?
#user.rb
class User < ApplicationRecord
COORDINATES = [[51.50853, -0.076132], [51.510357, -0.116773]]
def self.latitude
COORDINATES.map do |x|
x[0] # How to pass coordinates correctly?
end
end
def self.longitude
COORDINATES.map do |x|
x[-1] # How to pass coordinates correctly?
end
end
end
That's User controller index action:
def index
#latitude = User.latitude
#longitude = User.longitude
end
And that's index.html.erb. Here I provide #latitude and #longitude.
handler = Gmaps.build('Google');
handler.buildMap({ provider: {}, internal: {id: 'map'}}, function(){
markers = handler.addMarkers([
{
"lat": <%= #latitude %>,
"lng": <%= #longitude %>
}
]);
});

try this:
markers = handler.addMarkers([
<% User::COORDINATES.each.with_index do |geo,i| %>
{
"lat": <%= geo[0] %>,
"lng": <%= geo[1] %>
}
<% if User::COORDINATES[i+1] %>,<% end %>
<% end %>
]);

Related

Geocoding multiple addresses in one model

I am trying to geocode 2 addresses in a model using geocoder and I can't get gem to work as I want to. Here is the code that I am applying to my model:
class Sender < ActiveRecord::Base
validates_presence_of :source_address
validates_presence_of :destination_address
geocoded_by :source_address, :latitude => :latitude1, :longitude => :longitude1
geocoded_by :destination_address, :latitude2 => :latitude2, :longitude2 => :longitude2
def update_coordinates
geocode
[latitude1, longitude1, latitude2, longitude2]
end
after_validation :geocode
Here is code for views/senders/show.html.erb:
<%= #sender.latitude1 %>
<%= #sender.longitude1 %>
<%= #sender.latitude2 %>
<%= #sender.longitude2 %>
Result : 35.6894875 139.6917064 - Isn't it supposed to send me back 2 address information?
Here is my js:
<script type="text/javascript">
function initialize() {
var source = new google.maps.LatLng(<%= #sender.latitude1 %>, <%= #sender.longitude1 %>);
var dest = new google.maps.LatLng(<%= #sender.latitude2 %>, <%= #sender.longitude2 %>);
var mapOptions = {
center: source,
zoom: 8
}
var mapOptions2 = {
center: dest,
zoom: 8
}
var map = new google.maps.Map(document.getElementById('map_canvas'), mapOptions);
var map2 = new google.maps.Map(document.getElementById('map_canvas2'), mapOptions2);
var marker = new google.maps.Marker({
position:source,
map: map
});
var marker2 = new google.maps.Marker({
position:dest,
map: map2
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
The problem and solution are mentioned here.
Add the following before_save and corresponding method to your model to solve the problem. Don't forget to repeat the part of code for the second location (maybe destination):
before_save :geocode_endpoints
private
#To enable Geocoder to works with multiple locations
def geocode_endpoints
if from_changed?
geocoded = Geocoder.search(loc1).first
if geocoded
self.latitude = geocoded.latitude
self.longitude = geocoded.longitude
end
end
# Repeat for destination
if to_changed?
geocoded = Geocoder.search(loc2).first
if geocoded
self.latitude2 = geocoded.latitude
self.longitude2 = geocoded.longitude
end
end
end
Rewrite
def function
...
end
as:
def update_coordinates
geocode
[latitude, longitude, latitude2, longitude2]
end
And also:
geocoded_by :destination_address, :latitude => :latitude2, :longitude => :longitude2
You also don't need :latitude => :lat, :longitude => :lon here:
geocoded_by :source_address, ...
And finally, coordinates are fetched automatically after record is validated. So you could do without update_coordinates (or function, in your version) and arrange the view for show action like this:
<%= #sender.latitude %>
<%= #sender.longitude %>
<%= #sender.latitude2 %>
<%= #sender.longitude2 %>

How will be the best way to render array of arrays in erb template?

I have an array [["Company Name", "Field6"], ["Email", "Field5"]]
And from that array I am creating array of fields with values:
[
[{:label=>"Company Name", :value=>"gfdgfd"}],
[{:label=>"Email", :value=>"gfdgfd#gfd.pl"}]
]
using
fields = [["Company Name", "Field6"], ["Email", "Field5"]]
# first element in array is Label and second is param id
fields_with_values = fields.collect do |field|
[
label: field[0],
value: params[field[1]]
]
end
and then I want to pass that labels and values to erb template(something like):
# template.erb
<% fields_with_values.each do |field| %>
l: <%= field.label %>
v: <%= field.value %>
<% end %>
How will be the best way to collect these fields_with_values ? Maybe I should use Object.new
Convert to a hash instead.
fields = [["Company Name", "Field6"], ["Email", "Field5"]]
fields_with_values = Hash[*fields.flatten]
# => {"Company Name"=>"Field6", "Email"=>"Field5"}
In your view, parse the hash:
<% fields_with_values.each do |label, value| %>
l: <%= label %>
v: <%= params[value.intern] %>
<% end %>
Note that this will break if your input array is uneven, ie. a key without a value.
EDIT
As mentioned in a comment below (+1), duplicate keys will not work. Fields that have the same label as another field are no good.
fields = [["Company Name", "Field6"], ["Email", "Field5"]]
# first element in array is Label and second is param id
fields_with_values = fields.collect do |label, param_id|
# It looks like there is no need for a nested array here, so just return a Hash
{
label: label,
value: params[param_id]
}
end
#=> [{:label=>"Company Name", :value=>"gfdgfd"}, {:label=>"Email", :value=>"gfdgfd#gfd.pl"}]
It looks like you are trying to use dot syntax to get values out of a Ruby Hash similar to how you would use dot syntax for a JavaScript object (e.g. field.label). Unfortunately this doesn't work for Ruby. I wish it did because it looks very clean. For the Ruby Hash you must use an index, which is a symbol in this case: field[:label]. Your ERB code will look something like this:
# template.erb
<% fields_with_values.each do |field| %>
l: <%= field[:label] %>
v: <%= field[:value] %>
<% end %>
The easy most basic way would be:
class Foo
attr_accessors :label, :value
def initialize (label, value)
#label = label
#value = value
end
end
fields_with_values = fields.map do |field|
Foo.new(field[0], params[field[1]])
end
from here on you can make it more Ruby way with splat operator or create the objects on the fly, etc. etc.
l:
v:
I would do
fields_with_values = fields.collect do |field|
{label: field[0], value: params[field[1]}
end
And in the view
<% fields_with_values.each do |field| %>
l: <%= field[:label] %>
v: <%= field[:value] %>
<% end %>
However, lets say label is a company and value is an e-mail. If you have a class like
class Company < SomethingOrNothing
attr_accessible :name, email
# methods here
end
You could do
#companies = fields.collect do |field|
Company.new(name: field[0], email: field[1])
end
And then
<% #companies.each do |company| %>
l: <%= comapny.name %>
v: <%= company.email %>
<% end %>
However, most likely creating a new class just for that is over engineering, unless you will use this class over and over in your code.

accessors and update_attributes

I have a model with a location attribute, it's a array of two elements; latitude and longitude. I define the accessors for the location like this
class Address
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Spacial::Document
field :location, :type => Array, spacial: {lat: :latitude, lng: :longitude, return_array: true }
#accessors for location
def latitude
location[0]
end
def latitude=( lat )
location[0] = latitude
end
def longitude
location[1]
end
def longitude=( lng )
location[1] = lng
end
attr_accessible :location, :latitude, :longitude
end
here is the controller code
def create
#address = Address.new(params[:address])
if #address.save
redirect_to :action => 'index'
else
render :action => 'new'
end
end
def update
#address = Address.find(params[:id])
if #address.update_attributes(params[:address])
redirect_to :action => 'index'
else
render :action => 'edit'
end
end
and at the view level
<%= f.hidden_field :latitude%>
<%= f.hidden_field :longitude%>
these hidden field are manipulated via js, and that is ok. I saw it view developers tools
Here are the parameters the controller receives
"address"=>{"latitude"=>"-38.0112418", "longitude"=>"-57.53713060000001", "city_id"=>"504caba825ef893715000001", "street"=>"alte. brown", "number"=>"1234", "phone"=>"223 4568965"}, "commit"=>"Guardar", "id"=>"504cacc825ef893715000006"}
Note that the latitude and longitude parameters changed and thats ok, but this change it's not saved to the mongodb
So, The values for latitude and longitude are not saved. Is there any instruction my code is missing?
Thanks in advance.
--- edit ---
here are the working accessors
def latitude
location[0]
end
def latitude=( lat )
self.location = [lat,self.location[1]]
end
def longitude
location[1]
end
def longitude=( lng )
self.location = [self.location[0], lng]
end
When you want to set a field of your database, use self always safer and it's a good habit.
Second thing, you have to use the argument you pass to the setter.
Resulting code:
def latitude
location[0]
end
def latitude=( lat )
self.location[0] = lat
end
def longitude
location[1]
end
def longitude=( lng )
self.location[1] = lng
end

How to display a selected location along with all other locations using gmaps4rails?

I am trying to figure out why when I have a map displaying a controller's json string for map locations, when I click the infobox and go to a specific id page, I cannot zoom to the specific id location, and KEEP the rest of the locations on map. For instance:
The map displaying all of the points from the places_controller:
def index
#json = Places.all.to_gmaps4rails
end
The individual id page displaying itself:
def show
#json = Places.find(params[:id]).to_gmaps4rails
end
I want the .find(params[:id]).to_gmaps4rails to include .all of the places, but center on the :id location.
ps- the index map is geoLocated and centers on user, whereas the show location centers on just the 1 marker entry.
Thanks!
I'd do something like:
def show
#json = Places.all.to_gmaps4rails
#center = Places.find(params[:id])
end
And in view:
<%= gmaps( :map_options => { :center_latitude => #center.latitude, :center_longitude => #center.longitude, :zoom => 6 },
:markers => { :data => #json }
) %>

gmaps4rails display location based on latitude and longitude

I am using gmaps4rails with Rails 3.0.9. I would like to add a marker based on the latitude and longitude coordinates to the map, but I can't figure out how should I do it. I managed to use the map with an andress only. Thanks.
The code I'm using:
app/models/location.rb
class Location < ActiveRecord::Base
acts_as_gmappable :process_geocoding => false
def gmaps4rails_address
"#{self.latitude}, #{self.longitude}"
end
end
app/controllers/locations_controller.rb
def index
#json = Location.first.to_gmaps4rails
respond_to do |format|
format.html
end
app/views/locations/index.html.erb
<%= gmaps4rails(#json) %>
The location model contains the following fields: latitude, longitude, address.
UPDATE: I found the solution after reading one more time project's wiki about markers. The solution is to use custom <%= gmaps %> method. I managed to use it like this:
<%= gmaps({
"map_options" => { "type" => "ROADMAP", "center_longitude" => 28.9, "center_latitude" => 47.03, "zoom" => 12, "auto_adjust" => true},
"markers" => { "data" => #markers }
})
%>
In the controller:
#markers = '[
{"description": "", "title": "", "sidebar": "", "lng": "28.8701", "lat": "47.0345", "picture": "", "width": "", "height": ""},
{"lng": "28.9", "lat": "47" }
]'
You can customize these values as you like.
I think you should try geocoder gem instead of gmaps4rails
https://github.com/alexreisner/geocoder
http://www.rubygeocoder.com/
I think it is much better than gmaps4rails

Resources