Skip to content

Commit 3c8ae5b

Browse files
committed
introduce polar subplot attributes
1 parent 853581e commit 3c8ae5b

File tree

1 file changed

+327
-0
lines changed

1 file changed

+327
-0
lines changed

src/plots/polar/layout_attributes.js

+327
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
/**
2+
* Copyright 2012-2017, Plotly, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the MIT license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
'use strict';
10+
11+
var colorAttrs = require('../../components/color/attributes');
12+
var axesAttrs = require('../cartesian/layout_attributes');
13+
var extendFlat = require('../../lib').extendFlat;
14+
var overrideAll = require('../../plot_api/edit_types').overrideAll;
15+
16+
var domainItem = {
17+
valType: 'info_array',
18+
role: 'info',
19+
editType: 'plot',
20+
items: [
21+
{valType: 'number', min: 0, max: 1},
22+
{valType: 'number', min: 0, max: 1}
23+
],
24+
dflt: [0, 1]
25+
};
26+
27+
var axisLineGridAttr = overrideAll({
28+
color: axesAttrs.color,
29+
showline: extendFlat({}, axesAttrs.showline, {dflt: true}),
30+
linecolor: axesAttrs.linecolor,
31+
linewidth: axesAttrs.linewidth,
32+
showgrid: extendFlat({}, axesAttrs.showgrid, {dflt: true}),
33+
gridcolor: axesAttrs.gridcolor,
34+
gridwidth: axesAttrs.gridwidth
35+
36+
// should we add zeroline* attributes?
37+
// might be useful on radial axes where range is negative and positive
38+
39+
// we could add spike* attributes down the road
40+
}, 'plot', 'from-root');
41+
42+
var axisTickAttr = overrideAll({
43+
tickmode: axesAttrs.tickmode,
44+
nticks: axesAttrs.nticks,
45+
tick0: axesAttrs.tick0,
46+
dtick: axesAttrs.dtick,
47+
tickvals: axesAttrs.tickvals,
48+
ticktext: axesAttrs.ticktext,
49+
ticks: axesAttrs.ticks,
50+
ticklen: axesAttrs.ticklen,
51+
tickwidth: axesAttrs.tickwidth,
52+
tickcolor: axesAttrs.tickcolor,
53+
showticklabels: axesAttrs.showticklabels,
54+
showtickprefix: axesAttrs.showtickprefix,
55+
tickprefix: axesAttrs.tickprefix,
56+
showticksuffix: axesAttrs.showticksuffix,
57+
ticksuffix: axesAttrs.ticksuffix,
58+
showexponent: axesAttrs.showexponent,
59+
exponentformat: axesAttrs.exponentformat,
60+
separatethousands: axesAttrs.separatethousands,
61+
tickfont: axesAttrs.tickfont,
62+
tickangle: axesAttrs.tickangle,
63+
tickformat: axesAttrs.tickformat,
64+
tickformatstops: axesAttrs.tickformatstops,
65+
}, 'plot', 'from-root');
66+
67+
var radialAxisAttrs = {
68+
visible: extendFlat({}, axesAttrs.visible, {dflt: true}),
69+
type: axesAttrs.type,
70+
71+
// You thought maybe that range should only be a 'max' instead
72+
// as it always starts at 0? But, looks like off-zero cutout polar chart are
73+
// a thing:
74+
// -> mpl allow radial ranges to start off 0
75+
// -> same for matlab: https://www.mathworks.com/help/matlab/ref/rlim.html
76+
autorange: axesAttrs.autorange,
77+
// might make 'nonnegative' the default,
78+
// or add a special polar algo.
79+
rangemode: axesAttrs.rangemode,
80+
range: axesAttrs.range,
81+
82+
categoryorder: axesAttrs.categoryorder,
83+
categoryarray: axesAttrs.categoryarray,
84+
85+
// position (name analogous to xaxis.position),
86+
// or maybe something more specific e.g. angle angleoffset?
87+
//
88+
// (should this support any data coordinate system?)
89+
// I think it is more intuitive to set this as just an angle!
90+
// Thoughts?
91+
position: {
92+
valType: 'angle',
93+
editType: 'plot',
94+
role: 'info',
95+
description: [
96+
'Sets the angle (in degrees) from which the radial axis is drawn.',
97+
'Note that by default, radial axis line on the theta=0 line',
98+
'corresponds to a line pointing right (like what mathematicians prefer).',
99+
'Defaults to the first `polar.sector` angle.'
100+
].join(' ')
101+
},
102+
103+
side: {
104+
valType: 'enumerated',
105+
// maybe 'clockwise' and 'counterclockwise' would be best here
106+
values: ['left', 'right'],
107+
dflt: 'right',
108+
editType: 'plot',
109+
role: 'info',
110+
description: [
111+
'Determines on which side of radial axis line',
112+
'the tick and tick labels appear.'
113+
].join(' ')
114+
},
115+
116+
// not sure about these
117+
// maybe just for radialaxis ??
118+
title: axesAttrs.title,
119+
titlefont: axesAttrs.titlefont,
120+
121+
// only applies to radial axis for now (i.e. for cliponaxis: false traces)
122+
// but angular.layer could be a thing later
123+
layer: axesAttrs.layer,
124+
125+
hoverformat: axesAttrs.hoverformat,
126+
127+
// More attributes:
128+
129+
// We'll need some attribute that determines the span
130+
// to draw donut-like charts
131+
// e.g. https://github.com/matplotlib/matplotlib/issues/4217
132+
//
133+
// maybe something like 'span' or 'hole' (like pie, but pie set it in data coords?)
134+
// span: {},
135+
// hole: 1
136+
137+
// maybe should add a boolean to enable square grid lines
138+
// and square axis lines
139+
// (most common in radar-like charts)
140+
// e.g. squareline/squaregrid or showline/showgrid: 'square' (on-top of true)
141+
142+
editType: 'calc'
143+
};
144+
145+
extendFlat(
146+
radialAxisAttrs,
147+
148+
// N.B. the radialaxis grid lines are circular,
149+
// but radialaxis lines are straight from circle center to outer bound
150+
axisLineGridAttr,
151+
axisTickAttr
152+
);
153+
154+
var angularAxisAttrs = {
155+
visible: extendFlat({}, axesAttrs.visible, {dflt: true}),
156+
type: {
157+
valType: 'enumerated',
158+
// 'linear' should maybe be called 'angle' or 'angular' here
159+
// to make clear that axis here is periodic and more tightly match
160+
// `thetaunit`?
161+
//
162+
// no 'log' for now
163+
values: ['-', 'linear', 'date', 'category'],
164+
dflt: '-',
165+
role: 'info',
166+
editType: 'calc',
167+
description: [
168+
'Sets the angular axis type.',
169+
'If *linear*, set `thetaunit` to determine the unit in which axis value are shown.',
170+
'If *date*, set `period` to determine the wrap around period.',
171+
'If *category, set `period` to determine the number of integer coordinates around polar axis.'
172+
].join(' ')
173+
},
174+
175+
categoryorder: axesAttrs.categoryorder,
176+
categoryarray: axesAttrs.categoryarray,
177+
178+
thetaunit: {
179+
valType: 'enumerated',
180+
values: ['radians', 'degrees'],
181+
dflt: 'degrees',
182+
role: 'info',
183+
editType: 'calc',
184+
description: [
185+
'Sets the format unit of the formatted *theta* values.',
186+
'Has an effect only when `angularaxis.type` is *linear*.'
187+
].join(' ')
188+
},
189+
190+
period: {
191+
valType: 'any',
192+
editType: 'calc',
193+
role: 'info',
194+
description: ''
195+
196+
// 360 / 2*pi for linear (might not need to set it)
197+
// and to full range for other types
198+
199+
// 'period' is the angular equivalent to 'range'
200+
201+
// similar to dtick, one way to achieve e.g.:
202+
// - period that equals the timeseries length
203+
// http://flowingdata.com/2017/01/24/one-dataset-visualized-25-ways/18-polar-coordinates/
204+
// - and 1-year periods (focusing on seasonal change0
205+
// http://otexts.org/fpp2/seasonal-plots.html
206+
// https://blogs.scientificamerican.com/sa-visual/why-are-so-many-babies-born-around-8-00-a-m/
207+
// http://www.seasonaladjustment.com/2012/09/05/clock-plot-visualising-seasonality-using-r-and-ggplot2-part-3/
208+
// https://i.pinimg.com/736x/49/b9/72/49b972ccb3206a1a6d6f870dac543280.jpg
209+
// https://www.climate-lab-book.ac.uk/spirals/
210+
},
211+
212+
direction: {
213+
valType: 'enumerated',
214+
values: ['counterclockwise', 'clockwise'],
215+
// we could make the default 'clockwise' for date axes ...
216+
dflt: 'counterclockwise',
217+
role: 'info',
218+
editType: 'calc',
219+
description: [
220+
'Sets the direction corresponding to positive angles.'
221+
].join(' ')
222+
},
223+
224+
// matlab uses thetaZeroLocation: 'North', 'West', 'East', 'South'
225+
// mpl uses set_theta_zero_location('W', offset=10)
226+
//
227+
// position is analogous to yaxis.position, but as an angle (going
228+
// counterclockwise about cartesian y=0.
229+
position: {
230+
valType: 'angle',
231+
// we could maybe make `position: 90` by default for category and date angular axes.
232+
dflt: 0,
233+
editType: 'calc',
234+
role: 'info',
235+
description: [
236+
'Sets that start position (in degrees) of the angular axis',
237+
'Note that by default, polar subplots are orientation such that the theta=0',
238+
'corresponds to a line pointing right (like what mathematicians prefer).',
239+
'For example to make the angular axis start from the North (like on a compass),',
240+
'set `angularaxis.position` to *90*.'
241+
].join(' ')
242+
},
243+
244+
hoverformat: axesAttrs.hoverformat,
245+
246+
editType: 'calc'
247+
};
248+
249+
extendFlat(
250+
angularAxisAttrs,
251+
252+
// N.B. the angular grid lines are straight lines from circle center to outer bound
253+
// the angular line is circular bounding the polar plot area.
254+
axisLineGridAttr,
255+
// Note that ticksuffix defaults to '°' for angular axes with `thetaunit: 'degrees'`
256+
axisTickAttr
257+
);
258+
259+
module.exports = {
260+
// AJ and I first thought about a x/y/zoom system for paper-based zooming
261+
// but I came to think that sector span + radial axis range
262+
// zooming will be better
263+
//
264+
// TODO confirm with team.
265+
// x: {},
266+
// y: {},
267+
// zoom: {},
268+
269+
domain: {
270+
x: extendFlat({}, domainItem, {
271+
description: [
272+
'Sets the horizontal domain of this subplot',
273+
'(in plot fraction).'
274+
].join(' ')
275+
}),
276+
y: extendFlat({}, domainItem, {
277+
description: [
278+
'Sets the vertical domain of this subplot',
279+
'(in plot fraction).'
280+
].join(' ')
281+
}),
282+
editType: 'plot'
283+
},
284+
285+
// Maybe this should angularaxis.range correspond to
286+
// angular span of the drawing area?
287+
//
288+
// matlab's angular equivalent to 'range' bounds the drawing area
289+
// (partial circles as they call it)
290+
// https://www.mathworks.com/help/matlab/ref/thetalim.html
291+
//
292+
// as this attribute would be best set in (absolute) angles,
293+
// I think this should be set outside of angularaxis e.g
294+
// as polar.sector: [0, 180]
295+
sector: {
296+
valType: 'info_array',
297+
items: [
298+
// or be more strict -> `valType: 'angle' with `dflt: [0, 360]`
299+
{valType: 'number', editType: 'plot'},
300+
{valType: 'number', editType: 'plot'}
301+
],
302+
dflt: [0, 360],
303+
role: 'info',
304+
editType: 'plot',
305+
description: [
306+
'Sets angular span of this polar subplot with two angles (in degrees).',
307+
'Sector are assumed to be spanned in the counterclockwise direction',
308+
'with *0* corresponding to rightmost limit of the polar subplot.'
309+
].join(' ')
310+
},
311+
312+
bgcolor: {
313+
valType: 'color',
314+
role: 'style',
315+
editType: 'plot',
316+
dflt: colorAttrs.background,
317+
description: 'Set the background color of the subplot'
318+
},
319+
320+
radialaxis: radialAxisAttrs,
321+
angularaxis: angularAxisAttrs,
322+
323+
// TODO maybe?
324+
// annotations:
325+
326+
editType: 'calc'
327+
};

0 commit comments

Comments
 (0)