I have a bunch of geolocated points that I am looking to cluster on a google map using hexagons. These clusters will be stored in a db for quick retrieval and will be reduced to a single point and radius. I am using the http://www.geocodezip.com/v3_MW_example_eshapes.html library to draw the hexagons but because of the mercator projection the hexs overlap at higher latitudes.

Here is a jsfiddle attempt with the overlap http://jsfiddle.net/kworthin/Lkuqykog/2

overlapping hexes

  • So it seems the solution is hidden within the following library github.com/d3/d3-plugins/blob/master/hexbin/hexbin.js. Ultimately being able to determine which hex a map point should be placed by returning the lat/lng position of the hex based on a specific hex size would satisfy the objective.
    – Kevin
    Commented Oct 24, 2014 at 15:00
  • Here is a simpler example using squares jsfiddle.net/kworthin/388a1fb0 Surely someone knows how to prevent this overlap while keeping the shapes uniform. Apologies for calling you Surely :)
    – Kevin
    Commented Oct 28, 2014 at 3:06

1 Answer 1


If you're pregenerating the hex information, as your question suggests, you could use my R package dggridR to generate the hexagons' boundaries.

dggridR uses some rather complicated maths to tile the entire Earth with non-overlapping, equally-sized hexagons.

Here's an example of how you might use dggridR to create the grid you need:

#Include libraries

#Construct a global grid with cells approximately 10 miles across
dggs          <- dgconstruct(spacing=10, metric=FALSE, resround='down')

#Load included test data set
nylat = 40.7127 
nylon = -74.0059

#Generate a bunch of random points
lats <- runif(100,nylat-1,nylat+1)
lons <- runif(100,nylon-1,nylon+1)
df   <- data.frame(lat=lats, lon=lons)

#Get the corresponding hexagonal grid cells for each point
df$cell <- dgtransform(dggs,df$lat,df$lon)

#Get the number of points in each cell
pcounts <- df %>% group_by(cell) %>% summarise(count=n())

#Get the grid cell boundaries for the the points in a form suitable for plotting
grid    <- dgcellstogrid(dggs,df$cell,frame=TRUE)

#Update the grid cells' properties to include the number of points
grid    <- merge(grid,pcounts,by.x="Name",by.y="cell")

#Get a street map of the area
map   <- openmap(c(nylat+1,nylon-1),c(nylat-1,nylon+1),type='osm')
mapLL <- openproj(map)

#Plot everything on a flat map

p<- p+    geom_polygon(data=grid,      aes(x=long, y=lat, group=group, fill=count), alpha=0.4)    +
    geom_path   (data=grid,      aes(x=long, y=lat, group=group), alpha=0.4, color="white") +
    scale_fill_gradient(low="blue", high="red")

Discrete global grid on New York City

Okay, it works! Now let's save the grid, with the data, to somewhere it can be read by other programs:

#Get the grid cell boundaries in a form suitable for printing to a KML file
grid <- dgcellstogrid(dggs,df$cell,frame=FALSE)

#Update the grid cells' properties to include the number of earthquakes
#in each cell
grid@data$count <- merge(grid@data, pcounts, by.x="Name", by.y="cell", all.x=TRUE)$count

#Write out the grid
writeOGR(grid, "ny_grid.kml", "cells", "KML")
  • 2
    Thanks so much for sharing Richard. This is a super cool library you have written.
    – Kevin
    Commented Sep 5, 2018 at 17:42
  • Does this library require that the grid be pre-generated for the entire world? Or can an arbitrary set of bounds by chosen, and the bounds of a grid will be generated for that area? If so, say I chose a different set of bounds, will the second grid line up with the first if I was to later generate a grid in between them? Ie. I can select any arbitrary area and have a grid that is scaled, aligned, and positioned to match any other grid in any other area (I could make a "line" of hexagons between the areas and they would all align). Commented Jan 9, 2020 at 5:04
  • 1
    Can select regions to generate cells for but the cells are pinned to the globe and so generate the same regardless of order or union of generation regions.
    – Richard
    Commented Jan 9, 2020 at 5:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.