css

Wednesday, October 26, 2011

Google maps load markers into grid

A problem i recently encoutered was displaying more than 400 markers on a single map. Reading through the google maps api documentation i found the following post regarding this problem: http://code.google.com/apis/maps/.... The first solution mentioned is Grid-based Clustering which fits my needs. However the docs do not explain how to create such a grid. Here i explain what data is necessary to create a grid and load the markers into the grid.

First we need the boundaries of the current map:

var max_lng = map.getBounds().getNorthEast().lng();
var max_lat = map.getBounds().getNorthEast().lat();
var min_lng = map.getBounds().getSouthWest().lng();
var min_lat = map.getBounds().getSouthWest().lat();

These variables much be send to the page which returns the markers, this probably is already the case.

<cfquery name="GetMarkers" datasource="source">
SELECT id, lat, lng
FROM markers
WHERE
(<cfif min_lat lt max_lat>lat <= #max_lat# and lat>= #min_lat#<cfelse>lat <= #max_lat# OR lat>= #min_lat#</cfif>) AND
(<cfif min_lng lt max_lng>lon <= #max_lng# and lon>= #min_lng#<cfelse>lon <= #max_lng# OR lon>= #min_lng#</cfif>)
</cfquery>



<!--- Root of the number of grids: for example 10 means 100 grids --->
<cfset grid_count_root = 30 />
<cfset grid_min_lng = min_lng />
<cfset grid_min_lat = min_lat />
<!--- Array that contains the grid --->
<cfset grid_arr = arrayNew(2) />
<cfset grid_arr_lng = arrayNew(2) />
<cfset grid_arr_lat = arrayNew(2) />
<cfset grid_with_data = arrayNew(1) />
<cfset grid_lng_div = abs((min_lng-max_lng)/grid_count_root) />
<cfset grid_lat_div = abs((min_lat-max_lat)/grid_count_root) />

Now we are ready to create the grid:

<cfloop from=1 to=#grid_count_root# index="i">
 <cfset grid_arr_lng[i][1] = grid_min_lng />
 <cfset grid_arr_lng[i][2] = grid_min_lng + grid_lng_div />
 <cfset grid_arr_lat[i][1] = grid_min_lat />
 <cfset grid_arr_lat[i][2] = grid_min_lat + grid_lat_div />
 <cfset grid_min_lng = grid_min_lng + grid_lng_div />
 <cfset grid_min_lat = grid_min_lat + grid_lat_div />
</cfloop>

<cfloop from=1 to=#arraylen(grid_arr_lng)# index="i">
 <cfloop from=1 to=#arraylen(grid_arr_lng)# index="j">
  <cfset cur_pos = arrayLen(grid_arr)+1 />
  <cfset grid_arr[cur_pos][1] = grid_arr_lng[i][1] />
  <cfset grid_arr[cur_pos][2] = grid_arr_lng[i][2] />
  <cfset grid_arr[cur_pos][3] = grid_arr_lat[j][1] />
  <cfset grid_arr[cur_pos][4] = grid_arr_lat[j][2] />
  <cfset grid_arr[cur_pos][5] = '' />
  <cfset grid_arr[cur_pos][6] = '' />
  <cfset grid_arr[cur_pos][7] = '' />
  <cfset grid_arr[cur_pos][8] = 0 />
 </cfloop>
</cfloop>

Almost there now we load the markers into the grid

<cfloop from=1 to=#GetMarkers.recordcount# index="i">
 <cfset lng_grid_pos = #evaluate((GetMarkers.lng[i] -min_lng)/grid_lng_div)# />

 <cfif lng_grid_pos gt 0><cfset lng_grid_pos= #evaluate((ceiling(lng_grid_pos-1)*grid_count_root)+1)#>
 <cfelse><cfset lng_grid_pos = 1 /></cfif>

 <cfset lat_grid_pos = #evaluate((GetMarkers.lat[i] -min_lat)/grid_lat_div)# />

 <cfif lat_grid_pos gt 0><cfset lat_grid_pos= #evaluate((ceiling(lat_grid_pos-1)))#>
 <cfelse><cfset lat_grid_pos = 0 /></cfif>

 <cfset grid_pos = #evaluate(lng_grid_pos+lat_grid_pos)# />
 <cfif grid_arr[grid_pos][5] eq ''>
  <cfset grid_arr[grid_pos][5] = GetMarkers.id[i] />
  <cfset grid_arr[grid_pos][6] = GetMarkers.lat[i] />
  <cfset grid_arr[grid_pos][7] = GetMarkers.lng[i] />
  <cfset grid_with_data[arraylen(grid_with_data)+1] = grid_pos />
 </cfif>
 <cfset grid_arr[grid_pos][8] = grid_arr[grid_pos][8]+1 />
</cfloop>