[How to] Draw arrows on the map with Tableau !

draw arrows Map Tableau

Introduction

I don’t like writing intros. No one reads them anyway. Let me just say that today I will show you one of the ways you can draw arrows on the map, which I used for one of my personal projects, and which may be useful for you too. At the end of this tutorial, you will be able to make charts like this:

Let’s first talk out what we are going to do and the logic behind. Because if you understand that you can customize your arrows up to your taste (yes, I am not going to claim that my arrows are the most beautiful, although they most certainly are).

1. Polygons !

Polygons require ordered, enclosed sets of X, Y coordinates. This does mean that:

  • Each arrow will have its own unique ID
  • Each point of each arrow will have its own unique ID
  • These IDs will indicate in what order the line will be drawn through these points.

This is our arrow skeleton.

You can see 8 points here (the order matters!): A, B, C, Z, D, E, F, Z.

You may have noticed we have two Z here: for this kind of arrow, you need to have one value twice, otherwise your points will not be connected correctly. The last point will be connected with the first one automatically. Here, E is the end point and S is the start point in our data set.

We will need the following data source structure:

Arrow idPoint idXY
11(A1x coordinate)(A1y coordinate)
12(B1x coordinate)(B1y coordinate)
13(C1x coordinate)(C1y coordinate)
14(Z1x coordinate)(Z1y coordinate)
15(D1x coordinate)(D1y coordinate)
16(E1x coordinate)(E1y coordinate)
17(F1x coordinate)(F1y coordinate)
18(Z1x coordinate)(Z1y coordinate)
21(A2x coordinate)(A2y coordinate)
22(B2x coordinate)(B2y coordinate)

We will put Longitude in Columns, Latitude in Rows, choose Polygon as your type of chart, drag and drop Arrow id in Detail and Point id in Path.

2. Trigonomety :

To calculate coordinates, we will use the Pythagorean theorem and the following trigonometric formula:

    

On the example of the A point I will show you how to find all the coordinates, knowing the coordinates of S point (start point) and E point (end point) as well as desired length of AS (n).

We can calculate Ex-Sx and Ey-Sy since we know all the necessary coordinates. Thanks to that we can also calculate the length of ES and AE:

We can find Ey-Ay and Ex-Ax:

Then to find Ax you just need to subtract the value of Ex-Ax from the value of Ex. Same logic applies for Ay:

If you want to customize your arrow, just draw it differently on your canvas and calculate the coordinates using the same logic.

3. Longitude-latitude -> X-Y

  1. It’s also important to understand that longitude-latitude cannot be used for the 2D calculations as they are. First, we need to transform them in X-Y coordinates, then make all the calculations to find X-Y coordinates for all the points and finally transform them in longitude-latitude values.

This article explains the logic behind the calculations I used in my workbook.

https://medium.com/@suverov.dmitriy/how-to-convert-latitude-and-longitude-coordinates-into-pixel-offsets-8461093cb9f5

Here are the formulas we are going to use:

And reversed:

I used 1440 pixels as width value and 720 pixels as height value. I believe it doesn’t really matter what to choose since we will convert the coordinates back to longitude-latitude values.

Creating a dataset

Now it’s time to start. I will take longitude-latitude of 4 French cities and create arrows between them.

CityLatitudeLongitude
Paris48.854993571074592.3346734272193794
Lille50.623745916737623.0110800296755196
Lyon45.685543354282894.853667452230858
Bordeaux44.79603794403178-0.547927769672152

To do so, in my dataset I create all the possible pairs of these cities. Per each pair I will also define a randomly chosen n, that will define the thickness of the arrows. Here is what I’ve got:

City startStart LatitudeStart LongitudeCity endEnd LatitudeEnd Longitudenarrow id0
Paris48.854993571074592.3346734272193794Lille50.623745916737623.0110800296755196310
Paris48.854993571074592.3346734272193794Lyon45.685543354282894.853667452230858220
Paris48.854993571074592.3346734272193794Bordeaux44.79603794403178-0.547927769672152130
Lille50.623745916737623.0110800296755196Paris48.854993571074592.3346734272193794340
Lille50.623745916737623.0110800296755196Lyon45.685543354282894.853667452230858250
Lille50.623745916737623.0110800296755196Bordeaux44.79603794403178-0.547927769672152260
Lyon45.685543354282894.853667452230858Paris48.854993571074592.3346734272193794370
Lyon45.685543354282894.853667452230858Lille50.623745916737623.0110800296755196180
Lyon45.685543354282894.853667452230858Bordeaux44.79603794403178-0.547927769672152290
Bordeaux44.79603794403178-0.547927769672152Paris48.854993571074592.33467342721937943100
Bordeaux44.79603794403178-0.547927769672152Lille50.623745916737623.01108002967551961110
Bordeaux44.79603794403178-0.547927769672152Lyon45.685543354282894.8536674522308582120

Arrow id is a unique number per city start – city end pair. 0 column will be used to create a cartesian product with the second table. Remember, we need to have 8 lines per each row to draw our 8 points.

The second table looks like that:

0Point id
01
02
03
04
05
06
07
08

First, we need to create a data source. 12*8 gives us 96 rows:

After that you can hide both 0 columns, we won’t need them:

Calculations

Since I chose 1440 pixels as width value and 720 pixels as height, we can already put those values in our formulas:

Now we have everything to compute X-Y coordinates for S and E points:

Sx: 720+4*[Start Longitude]

Sy: 360-720*LN(TAN(PI()/4+[Start Latitude]*PI()/360))/PI()

Ex: 720+4*[End Longitude]

Ey: 360-720*LN(TAN(PI()/4+[End Latitude]*PI()/360))/PI()

Next step will be to calculate the distances between Sx and Ex, Sy and Ey, between S and E and A(C) and E.

We use ABS since our arrow can look at any direction and we want to calculate the distance between the points that can’t be negative.

Sx-Ex: ABS([Sx]-[Ex])

Sy-Ey: ABS([Sy]-[Ey])

D SE: SQRT(SQUARE([Sy-Ey])+SQUARE([Sx-Ex]))

D EA: SQRT(SQUARE([N])+SQUARE([D SE]))

Now we can start creating our points. We start with the point A. First, we calculate distances Ax-Ey and Ax-Ex:

Ay-Ey: [D EA]*SIN(ASIN([Sy-Ey]/[D SE])-ASIN([N]/[D EA]))

Ax-Ex: SQRT(SQUARE([D EA])-SQUARE([Ay-Ey]))

Then depending on the positioning of the end point regarding the start point we will either add the distance value to or subtract it from the coordinates of the end point:

Ay: IF [Ey] > [Sy] THEN [Ey]-[Ay-Ey] ELSE [Ey]+[Ay-Ey] END

Ax: IF [Ex] > [Sx] THEN [Ex]-[Ax-Ex] ELSE [Ex]+[Ax-Ex] END

We do the same procedure for other points.

Cx-Ex: [D EA]*SIN(ASIN([Sx-Ex]/[D SE])-ASIN([N]/[D EA]))

Cy-Ey: SQRT(SQUARE([D EA])-SQUARE([Cx-Ex]))

Cx: IF [Ex] > [Sx] THEN [Ex]-[Cx-Ex] ELSE [Ex]+[Cx-Ex] END

Cy: IF [Ey] > [Sy] THEN [Ey]-[Cy-Ey] ELSE [Ey]+[Cy-Ey] END

For the B point I defined SB = n/2. To calculate it I use the distance between Sy and Ay

By-Ay: SIN(ASIN(ABS([Sy]-[Ay])/[N])-PI()/6)*SQRT(5)*[N]/2

Bx-Ax: SQRT(5*SQUARE([N])/4-SQUARE([By-Ay]))

By: IF [Sy] > [Ay] THEN [Ay]+[By-Ay] ELSE [Ay]-[By-Ay] END

Bx: IF [Sx] > [Ax] THEN [Ax]+[Bx-Ax] ELSE [Ax]-[Bx-Ax] END

I made Z point depend on a constant m. My m=1

We calculate the coordinates of Z using the proportion D SE / m:

Zx-Ex: [Sx-Ex]*[m]/[D SE]

Zy-Ey: [Sy-Ey]*[m]/[D SE]

Zx: IF [Ex] > [Sx] THEN [Ex]-[Zx-Ex] ELSE [Ex]+[Zx-Ex] END

Zy: IF [Ey] > [Sy] THEN [Ey]-[Zy-Ey] ELSE [Ey]+[Zy-Ey] END

We have D and F points left.

Connecting them we will get the point O on the intersection with the SE line. I defined FO=OD=m=OE/2

Dx-Ex: SQRT(5)*[m]*SIN(ASIN([Zx-Ex]/[m])-PI()/6)

Dy-Ey: SQRT(5*SQUARE([m])-SQUARE([Dx-Ex]))

Dx: IF [Ex] > [Sx] THEN [Ex]-[Dx-Ex] ELSE [Ex]+[Dx-Ex] END

Dy: IF [Ey] > [Sy] THEN [Ey]-[Dy-Ey] ELSE [Ey]+[Dy-Ey] END

Fy-Ey: SQRT(5)*[m]*SIN(ASIN([Zy-Ey]/[m])-PI()/6)

Fx-Ex: SQRT(5*SQUARE([m])-SQUARE([Fy-Ey]))

Fy: IF [Ey] > [Sy] THEN [Ey]-[Fy-Ey] ELSE [Ey]+[Fy-Ey] END

Fx: IF [Ex] > [Sx] THEN [Ex]-[Fx-Ex] ELSE [Ex]+[Fx-Ex] END

Now we have all our X-Y coordinates and it’s time to convert them to longitude and longitude values.

Since I chose 1440 pixels as width value and 720 pixels as height, we can already put those values in our formulas:

Alat: (ATAN(EXP((360-[Ay])*PI()/720))-PI()/4)*360/PI()

Alon: ([Ax]-720)/4

Blat: (ATAN(EXP((360-[By])*PI()/720))-PI()/4)*360/PI()

Blon: ([Bx]-720)/4

Clat: (ATAN(EXP((360-[Cy])*PI()/720))-PI()/4)*360/PI()

Clon: ([Cx]-720)/4

Dlat: (ATAN(EXP((360-[Dy])*PI()/720))-PI()/4)*360/PI()

Dlon: ([Dx]-720)/4

Flat: (ATAN(EXP((360-[Fy])*PI()/720))-PI()/4)*360/PI()

Flon: ([Fx]-720)/4

Zlat: (ATAN(EXP((360-[Zy])*PI()/720))-PI()/4)*360/PI()

Zlon: ([Zx]-720)/4

Lastly, we need to create calculated fields to collect all the longitude and latitude values in a correct order.

Longitude: CASE [Point id] WHEN 1 THEN [Blon] WHEN 2 THEN [Clon] WHEN 3 THEN [Zlon] WHEN 4 THEN [Dlon] WHEN 5 THEN [End Longitude] WHEN 6 THEN [Flon] WHEN 7 THEN [Zlon] WHEN 8 THEN [Alon] END

Latitude: CASE [Point id] WHEN 1 THEN [Blat] WHEN 2 THEN [Clat] WHEN 3 THEN [Zlat] WHEN 4 THEN [Dlat] WHEN 5 THEN [End Latitude] WHEN 6 THEN [Flat] WHEN 7 THEN [Zlat] WHEN 8 THEN [Alat] END

Creating arrows

We need to change the geographic role for the last two calculated fields:

Now choose polygon chart, drag and drop Longitude in Columns, Latitude in Rows, Arrow id in Detail and Point id in Path.

You can also experiment with the colors, opacity, borders, and other features to make your arrows more beautiful.

Result:

Have fun !

Get a free demo of Tableau, the data visualization tool will help you see and undertand your data on one easy-to-use platform !

Author Profile

Olga Kutcenko
Olga Kutcenko

Write a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.