Last active
August 29, 2015 13:56
-
-
Save cblavier/8916592 to your computer and use it in GitHub Desktop.
Clustered geoloc search
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def within_box_events | |
# reading lower_left corner and upper_right corner parameters | |
ll_lng = Float(params[:lower_left][:lng]) | |
ll_lat = Float(params[:lower_left][:lat]) | |
ur_lng = Float(params[:upper_right][:lng]) | |
ur_lat = Float(params[:upper_right][:lat]) | |
# getting coordinates of a 9 times broader box, to get better clustering behavior at edges | |
q_ll_lng = [ll_lng - ((ur_lng - ll_lng) / 2), -180].max | |
q_ur_lng = [ur_lng + ((ur_lng - ll_lng) / 2), 180].min | |
q_ll_lat = [ll_lat - ((ur_lat - ll_lat) / 2), -90].max | |
q_ur_lat = [ur_lat + ((ur_lat - ll_lat) / 2), 90].min | |
# adjustement variable, the lower it is, the more events will be clustered | |
cluster_treshold = Float(params[:cluster_treshold]) rescue 50 | |
# when 2 events are closer than this distance, only the latest one is returned | |
min_distance = Float(params[:min_distance]) rescue 0.2 | |
max_age = Time.at(Integer(params[:max_age])) rescue 6.months.ago | |
# calculating cluster_distance: if distance between 2 events is lower than this, they are merged as a cluster | |
cluster_distance = Geocoder::Calculations.distance_between([ll_lat, ll_lng],[ur_lat, ur_lng], {:unit => :km}) / cluster_treshold | |
events = [] | |
clusters = [] | |
# iterating over all events within the box | |
Event.granted_for(current_user) | |
.where(:created_at.gt => max_age, :visible_picture_count.gte => 5) | |
.within_box(:location => [[ q_ll_lng, q_ll_lat], [ q_ur_lng, q_ur_lat]]) | |
.order_by(:created_at => :desc).each do |event| | |
current_event_in_cluster = false | |
# looking for existing clusters if current event must be merged into one of them | |
clusters.each do |cluster| | |
distance = distance_between_locations(event.location.to_hsh(:lng, :lat), cluster[:location]) | |
if distance < cluster_distance | |
if cluster[:event_locations].none?{ |location| distance_between_locations(location, event.location.to_hsh(:lng,:lat)) <= min_distance} | |
cluster[:count] += 1 if distance > min_distance | |
cluster[:event_locations] << event.location.to_hsh(:lng, :lat) | |
end | |
current_event_in_cluster = true | |
break | |
end | |
end | |
next if current_event_in_cluster | |
# looking for previous events if the 2 events must be merged into a single cluster | |
events.each_with_index do |previous_event, i| | |
distance = distance_between_locations(event.location.to_hsh(:lng, :lat), previous_event.location.to_hsh(:lng, :lat)) | |
if distance < cluster_distance | |
if distance > min_distance | |
clusters << {:count => 2, :location => event.location.to_hsh(:lng, :lat), :event_locations => [previous_event.location.to_hsh(:lng, :lat), event.location.to_hsh(:lng, :lat)], :cover_picture_url => event.cover_picture_url} | |
events.delete_at(i) | |
end | |
current_event_in_cluster = true | |
break | |
end | |
end | |
next if current_event_in_cluster | |
# if event has not been merged into a cluster, append it to the list | |
events << event | |
end | |
# removing any points not within the box | |
clusters.reject!{|c| c[:location][:lng] < ll_lng || c[:location][:lng] > ur_lng || c[:location][:lat] < ll_lat || c[:location][:lat] > ur_lat } | |
events.reject!{|e| e.location.x < ll_lng || e.location.x > ur_lng || e.location.y < ll_lat || e.location.y > ur_lat } | |
{ | |
:clusters => clusters.each{|cluster| cluster.delete(:event_locations) }, | |
:events => events | |
} | |
end | |
private | |
def distance_between_locations(location_1, location_2) | |
Geocoder::Calculations.distance_between( | |
[location_1[:lat], location_1[:lng]], | |
[location_2[:lat], location_2[:lng]], | |
{:unit => :km} | |
) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment