We're going to skip step 5 and use step 6 to get our feet wet with angular math. We'll draw angular grid lines for each month, circular grid lines for temperature, and label both axes.
To get our feet wet with this angular math, we'll draw our peripherals before we draw our data elements.
Let's switch those steps in our code.
If your first thought was "but the checklist!", here's a reminder that our chart drawing checklist is here as a friendly guide, and we can switch the order of steps if we need to.
Drawing the grid lines (peripheral) first is helpful in cases like this where we want our data elements to layer on top. If we wanted to keep our steps in order, we could also create a
<g> element first to add our grid lines to after.
Creating a group to hold our grid elements is also a good idea to keep our elements organized -- let's do that now.
Draw month grid lines#
Next, let's create one "spoke" for each month in our dataset. First, we'll need to create an array of each month. We already know what our first and last dates are -- they are the
.domain() of our
angleScale. But how can we create a list of each month between those two dates?
The d3-time module has various intervals, which represent various units of time. For example,
d3.timeMinute() represents every minute and
d3.timeWeek() represents every week.
Each of these intervals has a few methods -- we can see those methods in the documentation, and also if we double-click into the source code in our dev tools console.
For example, we could use the
.floor() method to get the first "time" in the current month:
d3 time intervals also have a
.range() method that will return a list of datetime objects, spaced by the specified interval, between two dates, passed as parameters.
Let's try it out by creating our list of months!
Great! Now we have an array of datetime objects corresponding to the beginning of each month in our dataset.
d3-time gives us shortcut aliases that can make our code even more concise -- we can use
d3.timeMonth() instead of
Let's use our array of months and draw one
<line> per month.
We'll need to find the angle for each month -- let's use our
angleScale to convert the date into an angle.
Each spoke will start in the middle of our chart -- we could start those lines at
[dimensions.boundedRadius, dimensions.boundedRadius], but most of our element will need to be shifted in respect to the center of our chart.
Remember how we use our bounds to shift our chart according to our
To make our math simpler, let's instead shift our bounds to start in the center of our chart.
This will help us when we decide where to place our data and peripheral elements -- we'll only need to know where they lie in respect to the center of our circle.
We'll need to convert from angle to
[x, y] coordinate many times in this chart. Let's create a function that makes that conversion for us. Our function will take two parameters:
and return the
[x,y] coordinates of a point rotated
angle radians around the center, and
offset time our circle's radius (
dimensions.boundedRadius). This will give us the ability to draw elements at different radii (for example, to draw our precipitation bubbles slightly outside of our temperature chart, we'll offset them by
1.14 times our normal radius length).
To convert an angle into a coordinate, we'll dig into our knowledge of trigonometry. Let's look at the right-angle triangle (a triangle with a 90-degree angle) created by connecting our origin point (
[0,0]) and our destination point (
The numbers we already know are theta (θ) and the hypotenuse (
dimensions.boundedRadius * offset). We can use these numbers to calculate the lengths of the
opposite sides of our triangle, which will correspond to the
y position of our destination point.