Created
August 7, 2014 07:20
-
-
Save christocracy/934b95e429e4f2086239 to your computer and use it in GitHub Desktop.
grid_clusterer.rb
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
module GeoKit | |
#It is half of the earth circumference in pixels at zoom level 21. | |
#You can visualize it by thinking of full map. Full map size is 536870912 × 536870912 pixels. | |
#Center of the map in pixel coordinates is 268435456,268435456 which in latitude and longitude would be 0,0. | |
PIXEL_OFFSET = 268435456 | |
PIXEL_RADIUS = PIXEL_OFFSET / Math::PI | |
module Mappable | |
module ClassMethods | |
def pixel_distance(from, to, zoom) | |
Math.sqrt((from[:x]-to[:x])**2 + (from[:y]-to[:y])**2).to_i >> (21-zoom) | |
end | |
def cluster(markers, grid_size, zoom) | |
# Scale grid-size based upon zoom | |
distance = grid_size - (grid_size/ 21 * zoom / 1.5) | |
# Once markers' pixel-distance become 4*grid_size, break from the each loop. | |
threshold = grid_size * 4 | |
clusters = [] | |
while(markers.count > 0) do | |
marker = markers.shift | |
cluster = [] | |
markers.each_index do |i| | |
pd = pixel_distance(marker, markers[i], zoom) | |
if distance > pd | |
cluster << markers.slice!(i) | |
elsif pd > threshold | |
break | |
end | |
end | |
if cluster.length > 0 | |
count = 0 | |
len = 0 | |
avg_lat = 0 | |
avg_lng = 0 | |
cluster.each do |i| | |
len += 1 | |
avg_lat += i[:lat] | |
avg_lng += i[:lng] | |
count += i[:count] | |
end | |
cluster.sort {|i| | |
i[:count] | |
} | |
last = cluster.last | |
clusters << ["cluster:#{last[:id].to_s}", avg_lat/len, avg_lng/len, count] | |
else | |
clusters << [marker[:id], marker[:lat], marker[:lng], marker[:count]] | |
end | |
end | |
clusters | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment