📜 ⬆️ ⬇️

How we did the polar graph in DevExtreme



Recently, our company introduced to the public a new version of the products, traditionally offering a large number of controls, features and buns. Our DevExtreme team was no exception, and one of the results of our fruitful work was the polar schedule. Why did we decide to do it, what problems did we encounter, and what did we end up with?

Why did we decide to make a polar schedule?


The polar graph is an extraordinary type of chart that is not used as often as line, pie and bar graphs. This is a graphical way of displaying multidimensional data as a two-dimensional diagram with three or more variables represented on axes starting from a single point. The polar graph is often called the radial chart, the radar chart, the cobweb . If we talk about polar graphics, an example of use immediately pops up in memory - this is a familiar wind rose.


Displaying data in the polar coordinate system

Charts with a polar coordinate system are one of the ways to display data, along with their display in a rectangular coordinate system. Essentially, almost any graph in a rectangular coordinate system can be displayed in the polar one and vice versa.
')


Polar Graph History


Where did such an intricate type of graphics come from?
If you think about it, all the ancient calendars, depicting time cyclically in a circle, are a peculiar example of the presentation of data in the polar coordinate system. Signs of the zodiac and constellations are also always depicted in a circle.

If we mention the official documents, then in 1801, William Playfer published a work called “The Statistical Breviary”, in which he used a kind of diagram in polar coordinates. They do not look like a modern view of the polar graphs, rather more like pie charts, but were the first step towards the polar graph.

In 1843, Leon Lalann used the polar graph to display the wind rose. Florence Nightingale in 1858 used a semblance of polar graphics to display information about the causes of death in the army.




Despite all this, primacy in the use of the polar graph is attributed to George von Mayer, who applied it in 1877.

And in 1997, Patrick Hoffman in his work on data processing introduces the term radial visualization . Since then, the polar graph is used in many modern infographics.
Of course, this is not a complete history of the emergence and use of this schedule, there are a large number of other examples. Such a schedule could not get into our field of vision, this is one of the reasons for the decision to implement it.

Disadvantages and advantages

Like all types of graphs, the polar graph has its drawbacks and advantages, which are described in great detail here in this book . The disadvantages include:





But besides the drawbacks of the polar graphics, there are advantages:







As you can see, the polar graph can be useful for data visualization, which also tipped the scales in favor of developing such a tool.

Widget line extension

From year to year we are expanding our range of visualization tools , trying to keep up with the times. We already have widgets like

Therefore, the appearance of the polar graph widget in our country was only a matter of time.

So, what reasons led our team to make a polar schedule:

What problems did we encounter on the way?


At the beginning of our work on the polar schedule, we thought, and how should it be? The first requirement that we put forward to it was its compliance with our entire line of widgets, namely, that it had a similar API, recognizable appearance and the same fast speed of work. The second thing we want is to spend the least amount of resources, people, time and money to create it.



To achieve all these goals, the most optimal solution was to write a new widget based on the usual dxChart chart widget, reusing its components.



Components such as the tooltip, legend and title look the same on any widget, so they could be reused without any changes. But the axis and the series needed to be modified, because now they had to be displayed in the polar coordinate system. These components perform two tasks — they work with data that is the same for any graphics, and draw themselves. It is in the drawing that lies the main difference.

On a par with “how to draw”, the aspect of “where to draw” should also have changed. In the widget of a regular schedule, we have a special module that, according to received data, returns coordinates on the screen. We call this module translator. Obviously, we had to create a new, polar translator that would know how to work in polar coordinates.



As a result, we identified our main problems:

Solving these problems, we would get a widget with a well-designed component architecture that would effectively solve emerging problems and expand its functionality, if necessary. Such a widget would allow you to create various complex graphics simply and quickly.

What have we got?


So, having solved all the problems that were in our way, we implemented a polar graph, creating a dxPolarChart widget that has a homogeneous structure with our other widgets and a flexible API that allows us to solve a wide range of tasks, as well as having fast speed of both work and rendering.

Now our polar graph supports such types of series as:




Such a tool, such as specifying a period for the axis of arguments, allows you to display cyclical data, and the axis of the arguments can be drawn both in a circle and a polygon, which is useful when creating a web graphic.



Let's see how to use our widget to implement the main scenarios for the use of polar graphics. All examples can be seen and touched here .

Graph comparing two objects by some criteria

On the polar graph it is very convenient to evaluate the characteristics of the objects among themselves. Let's compare two cat breeds according to the following criteria: weight, coat length, health, good nature, playfulness and intelligence.



How to get such a schedule? Let's start in order, with data and series. We describe the data used:
dataSource: [{ arg: "", cat1: 8, cat2: 5 }, { arg: " ", cat1: 7, cat2: 3 }, { arg: "", cat1: 7, cat2: 6 }, { arg: "", cat1: 7, cat2: 8 }, { arg: "", cat1: 6, cat2: 8 }, { arg: "", cat1: 8, cat2: 5 }] 
The cat1 field corresponds to information about the Maine Coon breed, and cat2 corresponds to the British Shorthair. We describe this in two series:
 series: [{ name: "-", valueField: "cat1" }, { name: " ", valueField: "cat2" }] 
The types of both series are areas with a stroke, so we will describe these properties for all series:
 commonSeriesSettings: { type: "area", border: { visible: true } } 
Since the evaluation in our graph is made on a ten-point scale, we add all these points to the scale of values:
 valueAxis: { categories: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] } 
Well, with the data sorted out, move on to the rest of the elements. Enable the tooltip:
 tooltip: { enabled: true } 
We describe the two-line header:
 title: "   \n   " 
Place the legend under the heading, and align the text to the right of the series markers:
  legend: { horizontalAlignment: "center", itemTextPosition: "right" } 
And the final touch, since we want to make a web graph, we describe the property:
 useSpiderWeb: true 

Here are the finished options
 options = { title: "   \n   ", useSpiderWeb: true, dataSource: [{ arg: "", cat1: 8, cat2: 5 }, { arg: " ", cat1: 7, cat2: 3 }, { arg: "", cat1: 7, cat2: 6 }, { arg: "", cat1: 7, cat2: 8 }, { arg: "", cat1: 6, cat2: 8 }, { arg: "", cat1: 8, cat2: 5 }], commonSeriesSettings: { type: "area", border: { visible: true } }, series: [{ name: "-", valueField: "cat1" }, { name: " ", valueField: "cat2" }], valueAxis: { categories: [1,2,3,4,5,6,7,8,9,10] }, legend: { horizontalAlignment: "center", itemTextPosition: "right" }, tooltip: { enabled: true } } 


Graph with cyclic time data

When it is necessary to compare data for some repetitive periods of time, the polar graph is the most productive to use. Suppose we need to compare sales data for the first two quarters of the year.



We describe the data:
 dataSource: [{ arg: 1, val: 12000000 },{ arg: 2, val: 14000000 },{ arg: 3, val: 13000000 },{ arg: 4, val: 18000000 },{ arg: 5, val: 21000000 },{ arg: 6, val: 16000000 }] 
The series is one here and has a line type
 series: { type: "line" } 
Let's write a two-line header:
 title: "  \n   " 
And turn off the legend, for one series, it is clearly superfluous here:
 legend: { visible: false } 
We will understand the axles. Let's start with the value axis. Since our data contains large numbers of sales, millions, to be more precise, we will present our signatures on the axes in this format:
 valueAxis: { label: { format: "millions" } } 
Now the axis of the arguments. First of all, we will set the period equal to 3 months, that is, one quarter. Next, we set the interval for the arguments, because we only need to show signatures for months. And so that the signatures have a postscript “month”, let them be casted.
 argumentAxis: { period: 3, tickInterval: 1, label: { customizeText: function(label) { return label.valueText + " "; } } } 

Here are the finished options
 options = { title: "  \n   ", dataSource: [{ arg: 1, val: 12000000 },{ arg: 2, val: 14000000 },{ arg: 3, val: 13000000 },{ arg: 4, val: 18000000 },{ arg: 5, val: 21000000 },{ arg: 6, val: 16000000 }], series: { type: "line" }, argumentAxis: { period: 3, tickInterval: 1, label: { customizeText: function(label) { return label.valueText + " "; } } }, valueAxis: { label: { format: "millions" } }, legend: { visible: false } } 


Wind rose chart

The display of the wind rose is a classic application of polar graphics.



Again, let's start with the data and series. Here is the data:
 dataSource: [{ arg: "", day: 8, night: 6 },{ arg: "", day: 3, night: 2 },{ arg: "", day: 1, night: 1 },{ arg: "", day: 0, night: 2 },{ arg: "", day: 4, night: 3 },{ arg: "", day: 2, night: 1 },{ arg: "", day: 4, night: 4 },{ arg: "", day: 6, night: 4 }] 
Here we have two series, one of which corresponds to the day field - data about the wind during the day, and the other to the night field - data about the wind at night. Also each series will have the corresponding color:
 series: [{ name: " ", valueField: "day", color: "#FFD700" }, { name: " ", valueField: "night", color: "#009ACD" }] 
Also, each of these series has a line type, and points of small size:
 commonSeriesSettings: { type: "line", point: { size: 6 } } 
Let's write a two-line header:
 title: "   \n   2014" 
Since we want the first argument, north, to be exactly on top, we indicate this in the axis of the arguments:
 argumentAxis: { firstPointOnStartAngle: true } 
Now we describe the axis of values. First of all, we point out that not the main, minor, marks on the axis have an interval of one day, so they look much clearer. Let's add a “d” to the signatures on the axis:
 minorTickInterval: 1, label: { customizeText: function(label) { return label.valueText + " "; } } 
It remains to specify the information about the calm on days and nights. To do this, we will use the usual broken lines, which will give their value, the corresponding color and the signature “Calm”:
 constantLines: [{ value: 5, color: "#FFD700", dashStyle: "dash", label: { text: "", font: { color: "#FFD700" } } }, { value: 7, color: "#009ACD", dashStyle: "dash", label: { text: "", font: { color: "#009ACD" } } }] 

Here are the finished options
 options = { title: "   \n   2014", dataSource: [{ arg: "", day: 8, night: 6 },{ arg: "", day: 3, night: 2 },{ arg: "", day: 1, night: 1 },{ arg: "", day: 0, night: 2 },{ arg: "", day: 4, night: 3 },{ arg: "", day: 2, night: 1 },{ arg: "", day: 4, night: 4 },{ arg: "", day: 6, night: 4 }], commonSeriesSettings: { type: "line", point: { size: 6 } }, series: [{ name: " ", valueField: "day", color: "#FFD700" }, { name: " ", valueField: "night", color: "#009ACD" }], argumentAxis: { firstPointOnStartAngle: true }, valueAxis: { minorTickInterval: 1, label: { customizeText: function(label) { return label.valueText + " "; } }, constantLines: [{ value: 5, color: "#FFD700", dashStyle: "dash", label: { text: "", font: { color: "#FFD700" } } }, { value: 7, color: "#009ACD", dashStyle: "dash", label: { text: "", font: { color: "#009ACD" } } }] } 


Quantitative comparison chart

Although the comparison of quantitative data is the prerogative of linear graphs, the polar graph can also be used for this purpose. For example, let's compare the number of people, men and women, in different countries.



Traditionally, let's start with the data and series. The data here looks like this:
 dataSource:[{ arg:"", usa_male: 134782000, usa_female: 140786000 },{ arg: "", brazil_male: 85127000, brazil_female: 87730000 },{ arg: "", rus_male: 68278000, rus_female: 64750000 },{ arg: "", japan_male: 52387000, japan_female: 64586000 },{ arg: "", ger_male: 40450000, ger_female: 42344000 },{ arg: "", gb_male: 23486000, gb_female: 30206000 }] 
We set as many series as sectors. Let's color them in the corresponding colors, and also for the convenience of setting further options, we will add the male tag to the series displaying data on men.
 series:[{ name: "-   ", valueField: "usa_male", color: "#9B30FF", tag: "male" },{ name: "-   ", valueField: "usa_female", color: "#7D26CD" },{ name: "-   ", valueField: "brazil_male", color: "#1E90FF", tag: "male" },{ name: "-   ", valueField: "brazil_female", color: "#1874CD" },{ name: "-   ", valueField: "rus_male", color: "#54FF9F", tag: "male" },{ name: "-   ", valueField: "rus_female", color: "#43CD80" },{ name: "-   ", valueField: "japan_male", color: "#FF6A6A", tag: "male" },{ name: "-   ", valueField: "japan_female", color: "#CD5555" },{ name: "-   ", valueField: "ger_male", color: "#FF7F24", tag: "male" },{ name: "-   ", valueField: "ger_female", color: "#CD661D" },{ name: "-   ", valueField: "gb_male", color: "#FFC125", tag: "male" },{ name: "-   ", valueField: "gb_female", color: "#CD9B1D" }] 
Each of these series has a stackedbar type. Almost all of them have a label showing the name of the country. Let's set these options for all series:
 commonSeriesSettings: { type: "stackedbar", label: { visible: true, font: { size: 14 }, customizeText: function(label){ return label.argumentText; } } } 
Now labels will be shown in all sectors, and we need them only for the upper ones, which display data on women. This is where our male tag comes in, which we added to the series. Disable labels from the series that have this tag:
 customizeLabel: function(label) { if (label.series.tag === "male") { return { visible: false } } } 
We describe the title:
 title: "   " 
Disable the legend:
 legend: { visible: false } 
Disable all axes, because we don’t need them here. The axis of the arguments then has the form:
 argumentAxis: { visible: false, label: { visible: false }, grid: { visible: false }, tick: { visible: false } } 
On the value axis, we also disable the upper indents using the valueMarginsEnabled option:
 valueAxis: { valueMarginsEnabled: false, visible: false, label: { visible: false }, grid: { visible: false }, minorGrid: { visible: false } } 
Great, it only remains to figure out the tooltip tooltip . Turn it on and paint it in the color of the dot above which it pops up. To do this, use the customizeTooltip option. Since the population is measured in millions, so as not to show such large numbers, the format millions is applicable to it. We also describe that the tooltip contains information about all points on the argument, that is, regardless of whether the pointer is pointing at men or women, all information will be displayed. For this, we use the shared option. Then the tooltip options will be like this:
 tooltip: { enabled: true, shared: true, font: { color: "#FFFFFF", size: 14 }, format: "millions", customizeTooltip: function(arg) { return { color: arg.point.getColor(), borderColor: arg.point.getColor() } } } 


Here are the finished options
 options = { title: "   ", dataSource:[{ arg:"", usa_male: 134782000, usa_female: 140786000 },{ arg: "", brazil_male: 85127000, brazil_female: 87730000 },{ arg: "", rus_male: 68278000, rus_female: 64750000 },{ arg: "", japan_male: 52387000, japan_female: 64586000 },{ arg: "", ger_male: 40450000, ger_female: 42344000 },{ arg: "", gb_male: 23486000, gb_female: 30206000 }], commonSeriesSettings: { type: "stackedbar", label: { visible: true, font: { size: 14 }, customizeText: function(label){ return label.argumentText; } } }, series:[{ name: "-   ", valueField: "usa_male", color: "#9B30FF", tag: "male" },{ name: "-   ", valueField: "usa_female", color: "#7D26CD" },{ name: "-   ", valueField: "brazil_male", color: "#1E90FF", tag: "male" },{ name: "-   ", valueField: "brazil_female", color: "#1874CD" },{ name: "-   ", valueField: "rus_male", color: "#54FF9F", tag: "male" },{ name: "-   ", valueField: "rus_female", color: "#43CD80" },{ name: "-   ", valueField: "japan_male", color: "#FF6A6A", tag: "male" },{ name: "-   ", valueField: "japan_female", color: "#CD5555" },{ name: "-   ", valueField: "ger_male", color: "#FF7F24", tag: "male" },{ name: "-   ", valueField: "ger_female", color: "#CD661D" },{ name: "-   ", valueField: "gb_male", color: "#FFC125", tag: "male" },{ name: "-   ", valueField: "gb_female", color: "#CD9B1D" }], legend: { visible: false }, argumentAxis: { visible: false, label: { visible: false }, grid: { visible: false }, tick: { visible: false } }, valueAxis: { valueMarginsEnabled: false, visible: false, label: { visible: false }, grid: { visible: false }, minorGrid: { visible: false } }, customizeLabel: function(label) { if (label.series.tag === "male") { return { visible: false } } }, tooltip: { enabled: true, shared: true, font: { color: "#FFFFFF", size: 14 }, format: "millions", customizeTooltip: function(arg) { return { color: arg.point.getColor(), borderColor: arg.point.getColor() } } } } 


More examples, demos and options can be viewed in our demo gallery .

findings


And in the new release, having conceived, implemented and solved all the problems, we present our new type of dxPolarChart graph. It allows to solve both specific tasks peculiar to this type of graphics, and routine tasks, replacing the usual types of graphs.

We tried to make this widget convenient to use, with an attractive, recognizable design, with high speed and rendering.

Our team hopes that the polar schedule will expand your ability to visualize data, and allow you to make your products better and more interesting. How well it turned out to realize the polar schedule, you decide, and we are always open to your wishes and constructive criticism.

Source: https://habr.com/ru/post/244359/


All Articles