Google maps - setting it up to work with Rails 5 - geocoder not geocoding

别等时光非礼了梦想. 提交于 2019-12-13 17:27:32

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!