Geokit plus Rails 3.1.1, lat and lon issue. - ruby-on-rails

I am using geokit-rails3 gem to find products in all colleges within range of particular college. A college has_many products and a product belong to college, there is another category model which has_many products and product belongs_to category. But when I try to find college from database on basis of addess using geokit it tell me lat coloumn is missing in my table.
Colleges migration is
create_table :colleges do |t|
t.string :name
t.text :address
t.string :city
t.string :state
t.integer :zipcode
t.timestamps
Controller
#products = College.within(5, :origin=>#current_product.college.address).product
Error:
Mysql::Error: Unknown column 'colleges.lat' in 'field list': SELECT `colleges`.*,
(ACOS(least(1,COS(0.3223824452162744)*COS(1.2891920858347756)*COS(RADIANS(colleges.lat))*COS(RADIANS(colleges.lng))+
COS(0.3223824452162744)*SIN(1.2891920858347756)*COS(RADIANS(colleges.lat))*SIN(RADIANS(colleges.lng))+
SIN(0.3223824452162744)*SIN(RADIANS(colleges.lat))))*3963.19)
AS distance FROM `colleges` WHERE ((colleges.lat>18.398868573573203 AND colleges.lat<18.543438426426793 AND colleges.lng>73.78905443427034 AND colleges.lng<73.94147656572967)) AND ((
(ACOS(least(1,COS(0.3223824452162744)*COS(1.2891920858347756)*COS(RADIANS(colleges.lat))*COS(RADIANS(colleges.lng))+
COS(0.3223824452162744)*SIN(1.2891920858347756)*COS(RADIANS(colleges.lat))*SIN(RADIANS(colleges.lng))+
SIN(0.3223824452162744)*SIN(RADIANS(colleges.lat))))*3963.19)
<= 5))
Any hint how to solve this issue?

Anything that acts_as_mappable with geokit needs a lat & lnt fields (these can be overridden using different names)
see the top of: http://geokit.rubyforge.org/readme.html
I'd recommend:
add_column :colleges, :lat, :float
add_column :colleges, :lng, :float
add_index :colleges, [:lat, :lng]
Also, to auto-update the address:
before_save :update_location
def update_location
#you probably you to use a full address, or join all the address parts instead of just self.address
loc=Geocoder.geocode(self.address)
if loc.success
self.lat = loc.lat
self.lng = loc.lng
end
end

You can also have different columns and map them to the acts_as_mappable this way:
acts_as_mappable :default_units => :miles,
:default_formula => :sphere,
:distance_field_name => :distance,
:lat_column_name => :lat,
:lng_column_name => :lng

Related

How to map one user to another (from same table)

I'd like to know how to solve this problem in my model/migrations, with correct referential integrity/uniqueness constraints.
I have a user table with two types of user: support_worker and service_user (like teacher and pupil). A support_worker can provide support for many service_users. I used to have separate tables for these respective user types, but for simplicity it makes more sense to have both user types in a single 'user' table (for Devise).
I'll have another table called support_allocation which records the relationship between a support_worker and the service_user(s) they support - this support_allocation has other information stored about it (like a budget; time/money). So this table needs to map one user_id to another user_id. I imagine the table structure will look something like this: SupportAllocation (id, support_worker_id, service_user_id)
So far, my migrations look like this (I've used Devise gem to create the user table so this amends it):
class ChangeUsers < ActiveRecord::Migration[5.2]
def change
change_table :users do |t|
t.string :user_type # support_worker or service_user
t.string :given_name
t.string :family_name
t.string :customer_reference # only for service_users
t.date :date_of_birth # only for service_users
t.string :job_roles # only for support_workers
end
end
class CreateSupportAllocations < ActiveRecord::Migration[5.2]
def change
create_table :support_allocations do |t|
t.boolean :active, default: true
# This next bit is guesswork
t.integer support_worker_id # support_worker's user_id
t.integer service_user_id # service_user's user_id
t.timestamps
end
end
end
Here's where I get confused... I need to create a join, but this will only do it on user_id, whereas the relationship is defined by the two user_id columns (as shown and named above). I'm not sure if this a compound key or if a single foreign key (or two) will suffice.
Here's my migration work-in-progress:
class AddJoins < ActiveRecord::Migration[5.2]
def change
change_table :support_allocations do |t|
t.belongs_to :user, index: true
end
end
end
I'd like to know how to achieve this. For the record, I'm using ActiveAdmin for my app. Thank you for your help.
I don't think you need the AddJoins migration. Add 2 associations in your CreateSupportAllocations model like so:
belongs_to :support_worker, :foreign_key => :support_worker_id, :class_name => User
belongs_to :service_user, :foreign_key => :service_user_id, :class_name => User
In your activeadmin form you can set the collections for the select, for example
(in app/admin/support_allocations.rb)
form do |f|
f.inputs do
# your inputs
f.input :support_worker, :as => :select, :collection => User.where(:user_type => 'support_worker')
f.input :service_user, :as => :select, :collection => User.where(:user_type => 'service_user')
end
f.actions
end
# added after comments
index do
selectable_column
column :support_worker
actions
end
Add a to_s method in you user model like so:
def to_s
"#{self.full_name}"
end
Thanks for all your help. I added the suggested associations to my SupportAllocation model. For the record, I also had to add the following associations to my User model to make the join work fully, in both directions:
has_many :occurances_as_support_worker, :class_name => 'SupportAllocation', :foreign_key => 'support_worker_id'
has_many :occurances_as_service_user, :class_name => 'SupportAllocation', :foreign_key => 'service_user_id'
I used the example given here to work this out.
When accessing attributes specific to a type of user (i.e. using the join over support_worker_id OR service_user_id), on the index page. I use code like this:
column 'Service user', :full_name, :sortable => 'service_users.family_name' do |support_allocation|
#ServiceUser.find(support_allocation.service_user_id).full_name
support_allocation.service_user.full_name
end
column 'Support worker', :full_name, :sortable => 'support_workers.family_name' do |support_allocation|
support_allocation.support_worker.full_name
end

Postgresql error when reverse geocoding in Ruby on Rails

I am having an issue trying to do a bulk reverse geocode using the geocoder rails gem: https://github.com/alexreisner/geocoder
I have the following models:
class School < ActiveRecord::Base
reverse_geocoded_by :latitude, :longitude
has_one :address
end
and
class Address < ActiveRecord::Base
belongs_to :school
end
And the following migrations:
class CreateSchools < ActiveRecord::Migration
def change
create_table :schools do |t|
t.string :name
t.integer :address_id
t.timestamps null: false
end
end
end
and
class CreateAddresses < ActiveRecord::Migration
def change
create_table :addresses do |t|
t.string :line_1
t.string :line_2
t.string :line_3
t.string :city
t.string :region
t.string :country
t.string :code
t.timestamps null: false
end
end
end
and when I run the following line:
rake geocode:all CLASS=School REVERSE=true SLEEP=0.5
I get this error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column schools.address does not exist
LINE 1: SELECT "schools".* FROM "schools" WHERE (schools.address IS...
^
: SELECT "schools".* FROM "schools" WHERE (schools.address IS NULL) ORDER BY "schools"."id" ASC LIMIT 1000
I know the readme says this:
"For geocoding your model must provide a method that returns an
address. This can be a single attribute, but it can also be a method
that returns a string assembled from different attributes (eg: city,
state, and country)."
I took that to mean I needed either a method on the School model or the attribute on the school table and I opted for the latter but I'm not sure what I'm missing.
Thanks!
The problem is that the reverse-geocoding rake task starts by loading all the records with no address column yet. It uses this scope:
scope :not_reverse_geocoded, lambda {
where("#{table_name}.#{geocoder_options[:fetched_address]} IS NULL")
}
The problem is you don't have any column on schools you could use. Instead, you should move the reverse_geocoded_by declaration to the Address class. You will also need to either add an addresses.address column or do something like this:
reverse_geocoded_by :latitude, :longitude, fetched_address: :line_1
Also you don't seem to have columns for latitude and longitude. And of course those should be on Address too, not School. After all, if a school can have several addresses, which one is its lonlat?

what is object to be geocoded and an array of Geocoder::Result objects in geocoder gem

I am trying to use geocoder gem but confused while using reverse geocoding.
I have "region" model with fields- country, state, city and zipcode.
If user fill only zipcode then i want to fill all other field automatically.
reverse_geocoded_by :latitude, :longitude do |obj,results|
if geo = results.first
obj.city = geo.city
obj.zipcode = geo.postal_code
obj.country = geo.country_code
end
end
after_validation :reverse_geocode
but unable to understand what obj and result is and how to set obj and result.
please help me by giving example.
i have written a blog explaining how it works.you need to geocode and get the relevant informations from geocoder gem.
User will enter address using Geocomplete and store that address in address column in users/locations table
Then,use Geocoder to fetch other geo informations and update other columns using address.
Here it goes................
================users/locations table,here i will use the allow the user to fill in the address using Geocomplete and then use it to get other details using Geocoder
class CreateLocations < ActiveRecord::Migration
def change
create_table :places do |t|
t.string :address
t.float :latitude
t.float :longitude
##======here the address field is important==========
t.string :address
t.string :country
t.string :state
t.string :city
t.string :pincode
t.timestamps
end
add_index :places, :address
end
end
==================Geocode to autopopulate using address in users/location model
##i want to use the address column to autopopulate others columns
geocoded_by :address
##also i want to use the latitude.longitude to fetch all others informations and then save in relevant ##feilds
reverse_geocoded_by :latitude, :longitude do |obj,results|
if geo = results.first
obj.state = geo.state
obj.city = geo.city
obj.pincode = geo.postal_code
obj.country = geo.country
end
end
##change/update/validate address only if address changed to improved performance else every time it ##will keep updating
after_validation :geocode, :reverse_geocode ,:if => :address_changed?

Rails 4: ActiveRecord => can't "select" has_one :through field from db

I am trying to retrieve one field from a "has_one :through" association. Assuming I have the following model:
class CreatePersons < ActiveRecord::Migration
def change
create_table :person do |t|
t.string :first_name
t.string :last_name
t.date :age
end
end
def change
create_table :location do |t|
t.decimal :longitude
t.decimal :latitude
t.string :city
t.string :state
t.references :person
end
end
def change
create_table :forecasts do |t|
t.timestamp :time
t.text :summary
t.decimal : precipitation
t.references :location
t.timestamps
end
end
end
... and the following model:
class Location < ActiveRecord::Base
belongs_to :person
belongs_to :forecast
end
class Forecast < ActiveRecord::Base
belongs_to :locaiton
has_one :person, :through => :location
end
class Person < ActiveRecord::Base
end
.. and I want to make query using ActiveRecord that pulls out ONLY the persons first name based on the forecast (i know its stupid, but this is just an excercise); so something like this:
# i realize this is a bad query, i'm putting this here to make my question stronger
Forecast.where("percipitation > ?", 1.5).select(:first_name)
or in SQL terms
select first_name from forecast fc
inner join locaiton loc on loc.id = fc.location_id
inner join person on person.id = loc.person_id
where precipitation > 1.5
SO I have tried something like this:
Forecast.joins(:person).where("percipitation > ?", 1.5).select(:person)
# Forecast.joins(:person).where("percipitation > ?", 1.5).select(:person).to_sql
# select 'person' from forecast fc
# inner join locaiton loc on loc.id = fc.location_id
# inner join person on person.id = loc.person_id
# where fc.percipitation > 1.5
this returns me empty instances of the Forecast object
so then I tried this:
Forecast.joins(:person).where("percipitation > ?", 1.5).select("person.first_name")
# Forecast.joins(:person).where("percipitation > ?", 1.5).select("person.first_name").to_sql
# select 'person.first_name' from forecast fc
# inner join locaiton loc on loc.id = fc.location_id
# inner join person on person.id = loc.person_id
# where fc.percipitation > 1.5
however, this also results in a colleciton of empty Forecast objects
HOWEVER, doing this results in exactly what i want, but this is already after the db has been queried:
result = Forecast.joins(:person).where("precipitation > ?", 1.5)
result.each do |forecast|
puts forecast.person.first_name # => "John", "Bob", "Jim"
end
Why can't I get just the first_name out of the DB using select and pull only first_name out of the database? I'm obviously missing something.
I don't know why your solution works at all.
Do it this way:
Forecast.where("precipitation > ?", 1.5).joins(:person).pluck("person.first_name")
It will return array of first names.
If you really need to use select (to get a scope):
Forecast.where("precipitation > ?", 1.5).joins(:person).select("person.first_name")

Trouble with self referential model in Rails

I have a model named User and I want to be able to self reference other users as a Contact. In more detail, I want a uni-directional relationship from users to other users, and I want to be able to reference an owned user of one user as a 'contact'. ALSO, i want to have information associated with the relationship, so I will be adding fields to the usercontact relation (I just edited this sentence in).
I attempted to do this while using the answer to this question as a guide.
Here is the User model:
user.rb
class User < ActiveRecord::Base
attr_accessible(:company, :email, :first_name, :last_name,
:phone_number, :position)
has_many(:user_contacts, :foreign_key => :user_id,
:dependent => :destroy)
has_many(:reverse_user_contacts, :class_name => :UserContact,
:foreign_key => :contact_id, :dependent => :destroy)
has_many :contacts, :through => :user_contacts, :source => :contact
end
I also created the model UserContact as a part of connecting contacts to users:
usercontact.rb
class UserContact < ActiveRecord::Base
belongs_to :user, :class_name => :User
belongs_to :contact, :class_name => :User
end
Here is the create_users.rb migration file i used:
create_users.rb
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :first_name
t.string :last_name
t.string :phone_number
t.string :email
t.string :company
t.string :position
t.timestamps
end
end
end
And here is the create_users_contacts.rb migration:
create_users_contacts.rb
class CreateUsersContacts < ActiveRecord::Migration
def up
create_table :users_contacts, :force => true do |t|
t.integer :user_id, :null => false
t.integer :contact_id, :null => false
t.boolean :update, :null => false, :default => false
end
# Ensure that each user can only have a unique contact once
add_index :users_contacts, [:user_id, :contact_id], :unique => true
end
def down
remove_index :users_contacts, :column => [:user_id, :contact_id]
drop_table :users_contacts
end
end
However, for reasons unknown to me, I believe something has gone awry in the linking since on my users index page, I have a column using <td><%= user.contacts.count %></td>, but I get this error from the line when I attempt to load the page:
uninitialized constant User::UserContact
I think the issue may be something to do with the fact that I want to name users associated with another user as contacts, because I cannot find other examples where that is done, and as far as I can tell I am doing everything properly otherwise (similarly to other examples).
The closest similar problem that I found was outlined and solved in this question. The issue was incorrect naming of his connecting model, however I double checked my naming and it does not have that asker's problem.
Any help is appreciated, let me know if any other files or information is necessary to diagnose why this is occurring.
EDIT
After changing usercontact.rb to user_contact.rb, I am now getting this error:
PG::Error: ERROR: relation "user_contacts" does not exist
LINE 1: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "...
^
: SELECT COUNT(*) FROM "users" INNER JOIN "user_contacts" ON "users"."id" = "user_contacts"."contact_id" WHERE "user_contacts"."user_id" = 1
EDIT TWO
The issue was that my linking table, users_contacts, was misnamed, and should have been user_contacts! so I fixed it, and now it appears to work!!
You need to rename your usercontact.rb to user_contact.rb
This is naming convention rails autoload works with.

Resources