A New Method for Creating Hex Maps
We love hex maps. We’ve written several blog posts about them and we use them all the time in both our personal and professional projects. Over the years, there have been several different methods for creating them in Tableau. Several years ago, Kevin wrote What the Hex? (A Brief History of the Hex) in which he walks through the various methods developed by the Tableau community—from Brittany Fong’s and Matt Chamber’s coordinates-based tile maps to the polygon file approach developed by Rody Zakovich to the spatial files created by Joshua Milligan and Luke Stanke. Another option not mentioned in the blog—since the blog is now 7 years old—was a method developed by Shaun Davis that built upon the coordinates-based solution and used calculated fields instead of an additional data file (Kevin mentions it in tip # 10 on Ten Tips including "Create a Hex Map Without Adding a Data Source).
Until recently, my favorite approach
has been the spatial file created by Luke Stanke, but Kevin and I recently
stumbled on a new approach that has since become our preferred method. In this
blog, I’m going to quickly review the various methods, detail some of the drawbacks
of each, then share the new technique.
Coordinates
This method entails joining or
relating your data set to a spreadsheet or text file that contains the geography
(we’ll be using US states) and the X and Y coordinates (Columns and Rows,
respectively).

We then plot the X and Y coordinates
using a scatterplot and a custom Hexagon shape.

Note # 1: The coordinates file that
is typically used requires that you reverse the Y axis. As it will simplify
things later, I’ve reversed the coordinates in the file itself so that this is
not necessary. If you’d like that file, you can find it here: US Hex Map Coordinates.xlsx
Note # 2: When using standard mark
types such as circles and squares, Tableau can automatically change the color
of the label (e.g. white on a dark background and black on a light background) in
order to increase the contrast between the mark color and the label color (making
it more readable). However, it cannot do this with custom shapes. To address
this, I’ve used a second axis using a circle mark type. And, in upcoming
examples, I’ll use a second map layer. For more details on this technique, see tip
# 2 on Ten Tips including "Show the Axis on the Top but Not the Bottom"
Unfortunately, there are several
issues with this approach:
1.
Separate Source – It requires a
separate file be added to your data source.
2.
Padding – You must
fiddle with the width and height of the chart to get the right padding/spacing
between marks.
3.
Dashboard Issues – When you add
the sheet to the dashboard, things can get messy, requiring that you adjust the
size of the sheet and the size of the marks.
4.
Filters – If you apply
filters, the marks completely disappear.
We can address the first issue by leveraging
a method developed by Shaun Davis. Instead of bringing in a separate file with
the coordinates, we create large CASE statements to get the Row and Column
values. The Row calc looks something like this:
CASE
[State/Province]
WHEN
'Alaska' THEN 0.0
WHEN
'Alabama' THEN -6.0
WHEN
'Arkansas' THEN -5.0
WHEN
'Arizona' THEN -5.0
WHEN
'California' THEN -5.0
WHEN
'Colorado' THEN -4.0
WHEN
'Connecticut' THEN -3.0
WHEN
'District of Columbia' THEN -5.0
WHEN
'Delaware' THEN -4.0
WHEN
'Florida' THEN -8.0
WHEN
'Georgia' THEN -7.0
WHEN
'Hawaii' THEN -8.0
WHEN
'Iowa' THEN -3.0
WHEN
'Idaho' THEN -3.0
...
The end result looks exactly the
same, but we’ve at least eliminated one of the issues detailed above. But what
about the other issues?
Spatial Files
Fortunately, Joshua Milligan and Luke
Stanke have come to our rescue!! They took the US hex map and turned it into a spatial
file—specifically, a shapefile. We relate this shapefile to our data, as shown
below.

Then we use the Geometry field
to automatically draw the map.

So, does this address the rest of our
issues? Actually, it does (though we’ll need a couple of tricks). The spatial
file automatically creates nice padding between each mark without the need for
us to fiddle with the width and height. And, when we add it to a dashboard, it
automatically resizes everything, keeping equal padding between the hexagons. One
potential concern might be that the padding is built into the shapefile, so you
can’t increase or decrease it. But this can be addressed by changing the mark
type to shape and using a hexagon shape. That also allows us to use different styles
of hexagons (I’ll share a few examples later).
Applying filters will still make the
marks completely disappear, but we can use a separate data source and map
layers to create a sort of static background map. To learn how to do that, see A Solution to the Hex Map Filtering Issue.
This method is pretty close to
perfect, but it still has a few drawbacks.
1. Refreshing on Cloud/Server
Last year, I discovered an issue with
refreshing extracted data sources on Cloud (and presumably Server). When the data
source includes tables from a database related to a spatial file, that data
source doesn’t properly refresh. All indications are that the refresh completed—the
job completes successfully and the data source extract dates get updated, but
if you dig into the data source, you’ll find that it didn’t actually refresh. I
haven’t tested this issue recently, so I’m not sure if it still exists, but my previous
experience (and how hard it is to see that it’s actually happening) has made me
very wary about creating data sources that relate tables from a database to a
spatial file. Ultimately, it would be nice to avoid the spatial file
altogether.
2. Other Hex/Tile Maps
I’ve seen and used lots of different
tile maps—Canada, Europe, Africa, the entire world, etc.—but, to my knowledge,
the only one that’s been converted to a spatial file is the United States. Creating
spatial files for all other hex/tile maps would certainly be doable, but it would
require using additional tools (a GIS platform) and specialized skills. I’m
okay with GIS, but I’d rather find a more Tableau-centric solution.
Spatial Coordinates
So how can we address these two problems?
The answer…spatial calculations!! Let’s go back to the Shaun Davis method—the one
that creates calculated fields for Row and Column. This data
source only has our primary data—no secondary file of coordinates or spatial
files. We’re going to take those Row and Column values and convert
them into geospatial coordinates. It doesn’t really matter that they aren’t
actually Latitude and Longitude values—we’re just going to trick them into
acting as Latitude and Longitude. Let’s create a calculated field that converts
these into a spatial point:
Point Coordinate
// Convert the X
& Y coordinates into geospatial coordinates.
MAKEPOINT([Row],
[Column])
We then simply drag that to our view,
in the same way as we did with the spatial file Geometry field.

Boom!! We have a hex map—one that did
not require any additional data files to be added to our data source and one
that can be created for any country, continent, etc. as long as we have the X
and Y coordinates.
But, there’s a slight problem. Notice
that there is more vertical spacing than horizontal spacing? This might not be
a big deal to most people, but it drives me crazy!! I must fix this!
Let’s Get a Little Wonky…
Warning: We’re going to get into some
low level technical stuff here, so feel free to skip to the end of the section
where I show you the updated calcs. If you want to get nerdy with me then, hell
yeah! Keep reading…
So, what’s going on here? Two things
actually. The first problem is that we’ve converted cartesian coordinates (X
and Y) to geospatial coordinates (Latitude and Longitude). These are very
different coordinate systems. The “distance” of a unit on a cartesian plane is
static. The distance between 0 to 1 is exactly the same as the distance between
1000 and 1001. And it doesn’t matter whether the coordinate is on the X axis or
the Y axis—0 to 1 on the X axis is the exact same distance as 0 to 1 on the Y
axis. This is because cartesian planes are two dimensional (i.e. flat) surfaces.
The globe, on the other hand, is a
three dimensional mostly-spherical object. The result is that the “distance”
of a unit in geospatial coordinates is not always equal. Latitude lines are parallel
lines running east and west. Because they are parallel, the distance between
units of Latitude is more or less static—around 69 miles (it varies a bit due
to the fact that the earth is not a perfect sphere—ranging from 68.703 miles at
the equator to 69.407 at the poles). Longitude lines are different—they run
north and south, converging at both the north and south poles. So, it follows
that the distance between Longitude lines are largest at the equator (69.172 miles)
and get smaller the closer you get to the poles.

That was a long explanation, but
important to know. That said, it ultimately doesn’t impact us that much—at least
not enough that we’d actually notice it visually. Our coordinates are all
relatively small (close-ish to zero) meaning that they are very close to the
equator. At the equator, the distance between units of Latitude and units of
Longitude are pretty close to the same value—close enough that we probably wouldn’t
be able to see the difference.
However, it is possible that someone might
make build a hex map with coordinates closer to the poles. For example, if we
adjusted our Rows to go from -100 to -108, the difference would be much more
noticeable.

This will plot our hex map
significantly closer to the south pole. Thus, the distances between lines of
Longitude are much smaller than the distances between lines of Latitude. In
other words, the 1 horizontal unit is much smaller than 1 vertical unit, which
leads to the smooshing we see above.
While we could figure out the exact
math to deal with these differences and adjust them—as Andy Cotgreave and I
detailed in his blog, How to make art out of your Strava activity data with Tableau—it’s overkill here.
Instead, we can just divide our Row and Column values by some arbitrarily
large number, making them very very close to zero. Let’s create calculated
fields for both of these new adjusted Latitudes and Longitudes:
Hex Latitude
[Column]/1000000
Hex Longitude
[Row]/1000000
With these values very close to zero,
there is pretty much no way we’ll ever notice the problem (at least until someone
creates a hex maps with huge numbers for coordinates!!!)
But this is only part of the problem
causing the spacing issues. The second problem is much simpler. The hexagon
shape is taller than it is wide. The shape I’m using is 504 pixels tall by 437
pixels wide—a ratio of about 1.1533. This is, of course, true of both the
coordinates method and the spatial file. With the coordinates approach, we just
resize the chart/sheet so that it’s wider than it is tall. Don’t believe me?
Here’s a hex map using the coordinates method where the axes have been set to
be more or less equal. Just like our spatial coordinates, the vertical padding
is much larger than the horizontal padding.

I’ll admit that I don’t 100% know how the spatial file addresses this problem, but Joshua and Luke clearly made adjustments so that the solution is baked into the files themselves.
Fortunately, the solution is simple—we
either divide the rows by the 1.1533 ratio or we multiple the columns by the
ratio. I’ll divide the rows, resulting in the following Longitude calc:
Hex Longitude
[Row]/1.1533/1000000
That ought to do it, so let’s combine
these together in our MAKEPOINT calc and try it out.
Point Coordinate
MAKEPOINT([Hex
Longitude], [Hex Latitude])

Yay, it works!
That was a long explanation, but
ultimately, the solution is pretty simple. Let’s recap:
1.
Create
the Column and Row calculated fields.
2.
Create
the Latitude and Longitude calculated fields, being sure to adjust
based on the shape size ratio and by a large number to keep the value close to
zero.
3.
Create
the geometry using MAKEPOINT.
Advantages
The advantage of this method are:
1.
No Separate File – No need for a
separate file in your data source.
2.
Padding Control – Complete
control over padding. Easily make it as large or small as you like.
3.
Dashboard – Limited issues
when adding to a dashboard. Note: Unlike spatial files, you’ll need to manually
resize the sheet and shapes once added to a dashboard.
4.
Equal Padding – No need to
fiddle with height and width of the chart to get the right padding in between
marks.
5.
Filters – Using the
technique mentioned earlier, you can ensure a mark is always visible, even if it’s
filtered out.
6.
Cloud/Server Refresh – Since no
secondary files are in the data source, there is no risk of issues when
refreshing the extract.
7.
Works for Anything – No need to use
a GIS tool to create a spatial file. If you have X and Y coordinates, you can
use this method to create your hex map.
8.
Styling – You can apply
any type of styling you want. And since it’s a map, you can have unlimited map
layers.
9.
Any Shape – This method
allows you to use any shape you like.
Let’s talk a bit about the last two advantages.
These are not exclusive to this new method because, as I shared earlier, you
can change the mark type when using a spatial file, allowing you to use
whatever shapes and layers you like. For example, here are a couple of alternative
hexagon shapes:


However, those shapes need to have the same height to width ratio as the hexagon we’ve been using. Otherwise, the built-in adjustments will not be accurate and will lead to padding differences. For example, if we choose a square mark using the spatial file, we’d see something like this:

The horizontal padding is much larger
than the vertical padding. It’s not bad at all, but I’m a perfectionist and I
really want it to be equal. With the new spatial coordinates technique, we can
simply adjust the ratio used in the Longitude calculation. In this case, since
the ratio between the height and width of a square is 1, we can eliminate it
altogether.
Hex Longitude
[Row]/1000000
And then we get a perfectly padded
square tile map.

Note: The alternative hexagons shown
above did not have the exact same ratio as the original hexagons, so the ratio used
in the calculations had to be adjusted to 1.1 in order to get equal padding.
Wrap Up
OK, that was a little bit wonky and I’m
sure everyone probably thinks I’m the most anal-retentive person alive (I need
that padding to be exact!! 😉)

But I do think this new method for creating hex/tile maps is the superior method. I hope you find it useful in your work. If you’re interested, you can find the workbook used here on Tableau Public.
Thanks, as always, for reading and if
you have any comments, please leave them in the comments section below.
Ken Flerlage, January
19, 2026
Twitter | LinkedIn | GitHub | Tableau Public











No comments: