问题
I have been trying for 4+ years to try and figure out a way to use google maps in my Rails app. I'm currently trying with Rails 5.
I recently posted this cry for help. The suggestions I received haven't worked for me, so I'm trying again now to see if someone else might know something. Rails 5, Gmaps4Rails - setup
I have models for organisation and address.
The associations are:
Organisation
has_many :addresses, as: :addressable
accepts_nested_attributes_for :addresses, reject_if: :all_blank, allow_destroy: true
Address
belongs_to :addressable, :polymorphic => true, optional: true
I'm no longer trying to figure this out with the gmaps4rails gem. I removed that from my gem file. I still have the geocoder gem though.
In my address controller, I have
def index
@addresses = Address.all
end
def show
end
In my organisation controller, I have
def index
@organisations = Organisation.all
authorize @organisations
end
def show
@addresses = @organisation.addresses.all
end
In my organisation view, I have
<%= render 'contacts/addresses/map' %>
In my address view, I have
<%= javascript_tag do %>
var addresses = <%= raw @addresses.to_json %>;
<% end %>
<script src="https://maps.googleapis.com/maps/api/js?key=<%= ENV["GOOGLE_MAPS_API_KEY"] %>&callback=initMap"
async defer></script>
In my address.js, I have
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4
});
var bounds = new google.maps.LatLngBounds();
var n = addresses.length;
for (var i = 0; i < n; i++) {
var address = new google.maps.Marker({
position: {lat: parseFloat(addresses[i].latitude), lng: parseFloat(addresses[i].longitude)},
title: addresses[i].name,
map: map
});
bounds.extend(address.position);
}
map.fitBounds(bounds);
}
While the zoom setting doesnt seem to work, this will actually render a map provided that I set the latitude and longitude fields from the console.
If I don't set those fields manually in the console, I get an error that says:
js?key=.....&callback=initMap:42 Uncaught RangeError: Maximum call stack size exceeded
This post Uncaught RangeError: Maximum call stack size exceeded Google maps when I try to set bounds suggests that problem is caused by the lat/lng coordinates not being passed to the js. That explains my problem because my console shows those attributes are saved as nil.
In my address model I have:
class Address < ApplicationRecord
geocoded_by :full_address # can also be an IP address
# --------------- associations
belongs_to :addressable, :polymorphic => true, optional: true
# --------------- scopes
# --------------- validations
# --------------- class methods
def unit_line
if self.unit.present?
['Unit', unit.titlecase].join(' ')
end
end
def first_line
[street_number, street.titlecase].join(' ')
end
def middle_line
if self.building.present?
['Building', building.titlecase].join(' ')
end
end
def last_line
[city.titlecase, region.upcase, zip].join(' ')
end
# def country_name
# # self.country = ISO3166::Country[country]
# # country.translations[I18n.locale.to_s] || country.name
# # iso_country = ISO3166::Country.find_by_name(country) # `country` should be name like 'Australia'
# # iso_country.translations[I18n.locale.to_s] || iso_country.name
# end
# def country_name
# self.country = ISO3166::Country[country]
# country.translations[I18n.locale.to_s] || country.name
# end
def country_name
country = self.country
ISO3166::Country[country]
end
def full_address
[self.unit_line, first_line, middle_line, last_line, country_name].compact.join("<br>").html_safe
end
# --------------- callbacks
after_create :geocode#, if self.full_address.changed?
I have also tried:
after_validation :geocode
# --------------- instance methods
# --------------- private methods
end
My address table has decimal attributes for latitude and longitude. I am using the geocoder gem and expect the address to be geocoded after creation.
I have an initialiser for geocoder.rb with:
Geocoder.configure(
# Geocoding options
:timeout => 3, # geocoding service timeout (secs)
:lookup => :google, # name of geocoding service (symbol)
:language => :en, # ISO-639 language code
:use_https => true, # use HTTPS for lookup requests? (if supported)
# :http_proxy => nil, # HTTP proxy server (user:pass@host:port)
# :https_proxy => nil, # HTTPS proxy server (user:pass@host:port)
:api_key => nil, # API key for geocoding service
# :cache => nil, # cache object (must respond to #[], #[]=, and #keys)
# :cache_prefix => "geocoder:", # prefix (string) to use for all cache keys
# exceptions that should not be rescued by default
# (if you want to implement custom error handling);
# supports SocketError and TimeoutError
# :always_raise => [],
# calculation options
:units => :km, # :km for kilometers or :mi for miles
# :distances => :linear # :spherical or :linear
)
Can anyone see what I need to do to get this to work?
Despite having an after validation on my address.rb asking for geocode, my lat/lng attributes are not updating with coordinates.
Console output
a = Address.first
Address Load (30.2ms) SELECT "addresses".* FROM "addresses" ORDER BY "addresses"."id" ASC LIMIT $1 [["LIMIT", 1]]
=> #<Address id: 8, unit: "16", building: "asdf", street_number: "15", street: "Johnston Street", city: "Balmain East", region: "NSW", zip: "2041", country: "AU", time_zone: "Midway Island", addressable_id: 1, addressable_type: "Organisation", description: "registered_office", created_at: "2016-11-10 00:16:50", updated_at: "2016-11-10 00:16:50", latitude: nil, longitude: nil>
Eric's suggestion
I tried to take Eric's suggestion to trigger the geocode, but it doesnt update the lat/lng coordinates.
Address.where(latitude: nil).or(Address.where(longitude: nil)).each{|marker| marker.save}
Address Load (16.3ms) SELECT "addresses".* FROM "addresses" WHERE ("addresses"."latitude" IS NULL OR "addresses"."longitude" IS NULL)
(2.0ms) BEGIN
(0.8ms) COMMIT
=> [#<Address id: 8, unit: "1", building: "asdf", street_number: "10", street: "Darling Street", city: "Balmain", region: "NSW", zip: "2041", country: "AU", time_zone: "Midway Island", addressable_id: 1, addressable_type: "Organisation", description: "registered_office", created_at: "2016-11-10 00:16:50", updated_at: "2016-11-10 00:16:50", latitude: nil, longitude: nil>]
回答1:
As usual, start simple and slowly add more code. I used the basic Rails app from my previous answer.
I added gem 'geocoder'
to Gemfile.
After bundle install
, I added
geocoded_by :name # can also be an IP address
after_validation :geocode
to my Marker class, which already has latitude and longitude defined as :decimal attributes.
In rails c
:
Marker.create(:name => 'New Dehli')
Marker.create(:name => 'New-York')
Done, they appear on the map!
NOTE: If for some reason some Marker doesn't have coordinates, the map will not be displayed, and you'll get a "too much recursion" Error in js console.
To avoid this, you can make sure all your Markers have coordinates by enabling after_validation :geocode
in your model, and launching this in in your console :
Marker.where(latitude: nil).or(Marker.where(longitude: nil)).each{|marker| marker.save}
Saving the marker triggers the validation, which triggers the geocoding.
If for some reason you still have non-geocoded Markers, change your js to :
function initMap() {
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 4
});
var bounds = new google.maps.LatLngBounds();
var n = markers.length;
for (var i = 0; i < n; i++) {
var lat = markers[i].latitude;
var lng = markers[i].longitude;
if (lat == null || lng ==null){
console.log(markers[i].name + " doesn't have coordinates");
}else {
var marker = new google.maps.Marker({
position: {lat: parseFloat(lat), lng: parseFloat(lng)},
title: markers[i].name,
map: map
});
bounds.extend(marker.position);
}
}
map.fitBounds(bounds);
}
来源:https://stackoverflow.com/questions/40518141/google-maps-setting-it-up-to-work-with-rails-5-geocoder-not-geocoding