Geocoder rails, check if valid
Asked Answered
B

5

6

Below is my class that I am using geocoder with. I am having trouble checking whether or not address that is put through geocoder is valid. This code works great when I am adding a company/editing a company that has addresses that when put through geocoder gives a latitude and longitude. But I need the code to work so when a user tries to put a address that does not give a latitude and longitude, on either an edit or creation of a company, that an error pops up and informs the user that the address is invalid.

Model

class Company < ActiveRecord::Base
    validates :name, presence: true, length: { maximum: 30 }
    validates :website, presence: true
    validates :address, presence: true
    validates :description, presence: true
    validates :primary_field, presence: true

    geocoded_by :address
    before_save :geocode, if: ->(obj){ obj.address.present? and obj.address_changed? }

    after_save :set_popup_value

    private

     ...

end

View

<% provide(:title, 'Add Company') %>
<h1>Add Company</h1>
<div class="row">
  <div class="span6 offset3">
    <%= form_for(@company) do |f| %>
      <%= render 'shared/error_messages' %>

      <%= f.label :name %>
      <%= f.text_field :name %>

      <%= f.label :website %>
      <%= f.text_field :website %>

      <%= f.label :primary_field %>
      <%= f.select :primary_field, @primary_field %>

      <%= f.label :address %>
      <%= f.text_field :address, :placeholder => '123 Test St, City State Zip'%>

      <%= f.label :description %>
      <%= f.text_area :description, :size => "30x5" %>

      <%= f.submit "Add Company", class: "btn btn-large btn-primary" %>
    <% end %>
  </div>
</div>
Bangui answered 15/4, 2014 at 17:22 Comment(2)
Are you asking SO to write the address validation code for you?Foxe
No, I just don't know where/how I would check to to see if an address is valid. So basically, when a user hits the "Add Company" button, if the user input an address that geocoder can't get lat/long back from, than I need to raise an error. But I am not sure how to do this.Bangui
B
3

So, having been able to tinker around with this for a few days I have come up with a couple solutions. The first was really hacky, but accomplished what I needed. My second solution is much more elegant and works great from what I have been able to tell so far! I hope others find this useful.

I handle all of the logic in my controller in just a few lines.

geocoded_by :address
after_validation :geocode, if: ->(obj){ obj.address.present? and obj.address_changed? }
after_validation :lat_changed?

after_save :set_popup_value

private

# This is used to make sure that our address is actually parsed properly and we
# get a valuable lat/long
def lat_changed?
    if (self.address_changed?)
        if !(self.latitude_changed?)
            self.errors.add(:address, "is not valid")
            return false
        end
    end
    return true
end

By creating another after_validation after my geocode, I am able to check if the appropriate values changed, and if they changed in a certain way, than I know there was an issue in the geocoding.

If the address changed, but the latitude didnt change, than you know that when the address got put through geocoder that it didn't get a returned lat/long. And if that is the case then we just add an error to throw to the user and return false, which stops the save to the database.

I hope others with this issue are able to find this post!

Bangui answered 18/4, 2014 at 5:9 Comment(3)
You can improve it doing: def lat_changed? ` if address_changed? && !latitude_changed?` ` self.errors.add(:address, "is not valid")` ` end` endBurns
I think it's not good idea to add error after validation callback.Sericin
This is making an assumption that latitude has to be changed if an address it changed. Is this necessarily true?Halfwitted
S
2

You can set in the model like this example below to validate address the geocode will be called only once when the address changed. In geocoded_by method we set explicitly to write latitude and longitude so than when the address would not be found those columns will be set to nil.

class Company < ActiveRecord::Base
   geocoded_by :address do |object, results|
    if results.present?
     object.latitude = results.first.latitude
     object.longitude = results.first.longitude
    else
     object.latitude = nil
     object.longitude = nil
    end
  end

  before_validation :geocode, if: :address_changed?

  validates :address, presence: true
  validates :found_address_presence

  def found_address_presence
    if latitude.blank? || longitude.blank?
      errors.add(:address, "We couldn't find the address")
    end
  end
end
Sericin answered 15/3, 2017 at 16:13 Comment(0)
F
1

you're going to probably want to do a web/service call to the geocoder service. How you do this depends on what you want: ajaxy request or background job,etc.

This is a pretty general question for SO, you might get some better traction in programmers.stackexchange.com

But, here are some links to help you out:

Google Geocoding API: https://developers.google.com/maps/documentation/geocoding/?csw=1

OpenStreetMap: http://wiki.openstreetmap.org/wiki/Nominatim#Reverse_Geocoding_.2F_Address_lookup

Foxe answered 15/4, 2014 at 18:24 Comment(0)
B
1

Since you're using Geocoder, you just need to have an opportunity to check the result of geocoder before saving. This process is explained in the section "Advanced geocoding" in it's github page. To just provide an example, you can do this:

geocoded_by :address do |obj,results|
  ....
end

After validation of the result, if you haven't obtained your coordinates, you can make your exception/add validation error/whatever.

Of course this will mean, that you have to call web service to see, if the address is ok - like others say, it would be nice to have some basic validation done before this.

Backwoodsman answered 15/4, 2014 at 18:32 Comment(0)
B
0

What Sean Callahan said is ok. You can improve the code doing:

def lat_changed?
  if address_changed? && !latitude_changed?
    self.errors.add(:address, "is not valid")
  end
end

Is not necessary to return true/false

Burns answered 12/4, 2016 at 15:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.