2 * @class Ext.chart.series.Radar
3 * @extends Ext.chart.series.Series
5 * Creates a Radar Chart. A Radar Chart is a useful visualization technique for comparing different quantitative values for
6 * a constrained number of categories.
7 * As with all other series, the Radar series must be appended in the *series* Chart array configuration. See the Chart
8 * documentation for more information. A typical configuration object for the radar series could be:
10 * {@img Ext.chart.series.Radar/Ext.chart.series.Radar.png Ext.chart.series.Radar chart series}
12 * var store = Ext.create('Ext.data.JsonStore', {
13 * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'],
15 * {'name':'metric one', 'data1':10, 'data2':12, 'data3':14, 'data4':8, 'data5':13},
16 * {'name':'metric two', 'data1':7, 'data2':8, 'data3':16, 'data4':10, 'data5':3},
17 * {'name':'metric three', 'data1':5, 'data2':2, 'data3':14, 'data4':12, 'data5':7},
18 * {'name':'metric four', 'data1':2, 'data2':14, 'data3':6, 'data4':1, 'data5':23},
19 * {'name':'metric five', 'data1':27, 'data2':38, 'data3':36, 'data4':13, 'data5':33}
23 * Ext.create('Ext.chart.Chart', {
24 * renderTo: Ext.getBody(),
82 * In this configuration we add three series to the chart. Each of these series is bound to the same categories field, `name` but bound to different properties for each category,
83 * `data1`, `data2` and `data3` respectively. All series display markers by having `showMarkers` enabled. The configuration for the markers of each series can be set by adding properties onto
84 * the markerConfig object. Finally we override some theme styling properties by adding properties to the `style` object.
88 Ext.define('Ext.chart.series.Radar', {
90 /* Begin Definitions */
92 extend: 'Ext.chart.series.Series',
94 requires: ['Ext.chart.Shape', 'Ext.fx.Anim'],
99 alias: 'series.radar',
107 * @cfg {Object} style
108 * An object containing styles for overriding series styles from Theming.
112 constructor: function(config) {
113 this.callParent(arguments);
115 surface = me.chart.surface, i, l;
116 me.group = surface.getGroup(me.seriesId);
117 if (me.showMarkers) {
118 me.markerGroup = surface.getGroup(me.seriesId + '-markers');
123 * Draws the series for the current chart.
125 drawSeries: function() {
127 store = me.chart.substore || me.chart.store,
131 animate = chart.animate,
132 field = me.field || me.yField,
133 surface = chart.surface,
134 chartBBox = chart.chartBBox,
145 l = store.getCount(),
146 startPath, path, x, y, rho,
148 seriesStyle = me.seriesStyle,
149 seriesLabelStyle = me.seriesLabelStyle,
150 first = chart.resizing || !me.radar,
151 axis = chart.axes && chart.axes.get(0),
152 aggregate = !(axis && axis.maximum);
156 maxValue = aggregate? 0 : (axis.maximum || 0);
158 Ext.apply(seriesStyle, me.style || {});
160 //if the store is empty then there's nothing to draw
161 if (!store || !store.getCount()) {
165 me.unHighlightItem();
166 me.cleanHighlights();
168 centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
169 centerY = me.centerY = chartBBox.y + (chartBBox.height / 2);
170 me.radius = radius = Math.min(chartBBox.width, chartBBox.height) /2;
171 me.items = items = [];
174 //get all renderer fields
175 chart.series.each(function(series) {
176 fields.push(series.yField);
178 //get maxValue to interpolate
179 store.each(function(record, i) {
180 for (i = 0, nfields = fields.length; i < nfields; i++) {
181 maxValue = max(+record.get(fields[i]), maxValue);
185 //ensure non-zero value.
186 maxValue = maxValue || 1;
187 //create path and items
188 startPath = []; path = [];
189 store.each(function(record, i) {
190 rho = radius * record.get(field) / maxValue;
191 x = rho * cos(i / l * pi2);
192 y = rho * sin(i / l * pi2);
194 path.push('M', x + centerX, y + centerY);
195 startPath.push('M', 0.01 * x + centerX, 0.01 * y + centerY);
197 path.push('L', x + centerX, y + centerY);
198 startPath.push('L', 0.01 * x + centerX, 0.01 * y + centerY);
201 sprite: false, //TODO(nico): add markers
202 point: [centerX + x, centerY + y],
209 me.radar = surface.add(Ext.apply({
213 }, seriesStyle || {}));
216 if (chart.resizing) {
217 me.radar.setAttributes({
223 me.onAnimate(me.radar, {
226 }, seriesStyle || {})
229 me.radar.setAttributes(Ext.apply({
231 }, seriesStyle || {}), true);
233 //render markers, labels and callouts
234 if (me.showMarkers) {
241 // @private draws the markers for the lines (if any).
242 drawMarkers: function() {
245 surface = chart.surface,
246 markerStyle = Ext.apply({}, me.markerStyle || {}),
247 endMarkerStyle = Ext.apply(markerStyle, me.markerConfig),
249 type = endMarkerStyle.type,
250 markerGroup = me.markerGroup,
251 centerX = me.centerX,
252 centerY = me.centerY,
255 delete endMarkerStyle.type;
257 for (i = 0, l = items.length; i < l; i++) {
259 marker = markerGroup.getAt(i);
261 marker = Ext.chart.Shape[type](surface, Ext.apply({
274 if (chart.resizing) {
275 marker.setAttributes({
292 me.onAnimate(marker, {
297 marker.setAttributes(Ext.apply(marker._to, endMarkerStyle || {}), true);
302 isItemInPoint: function(x, y, item) {
307 return (abs(point[0] - x) <= tolerance &&
308 abs(point[1] - y) <= tolerance);
311 // @private callback for when creating a label sprite.
312 onCreateLabel: function(storeItem, item, i, display) {
314 group = me.labelsGroup,
316 centerX = me.centerX,
317 centerY = me.centerY,
319 endLabelStyle = Ext.apply(me.seriesLabelStyle || {}, config);
321 return me.chart.surface.add(Ext.apply({
323 'text-anchor': 'middle',
330 // @private callback for when placing a label sprite.
331 onPlaceLabel: function(label, storeItem, item, i, display, animate) {
334 resizing = chart.resizing,
336 format = config.renderer,
337 field = config.field,
338 centerX = me.centerX,
339 centerY = me.centerY,
347 label.setAttributes({
348 text: format(storeItem.get(field)),
354 label.setAttributes({
362 me.onAnimate(label, {
366 label.setAttributes(opt, true);
371 // @private for toggling (show/hide) series.
372 toggleAll: function(show) {
374 i, ln, shadow, shadows;
376 Ext.chart.series.Radar.superclass.hideAll.call(me);
379 Ext.chart.series.Radar.superclass.showAll.call(me);
382 me.radar.setAttributes({
386 if (me.radar.shadows) {
387 for (i = 0, shadows = me.radar.shadows, ln = shadows.length; i < ln; i++) {
389 shadow.setAttributes({
397 // @private hide all elements in the series.
398 hideAll: function() {
399 this.toggleAll(false);
403 // @private show all elements in the series.
404 showAll: function() {
405 this.toggleAll(true);
408 // @private hide all markers that belong to `markerGroup`
409 hideMarkers: function(index) {
411 count = me.markerGroup && me.markerGroup.getCount() || 0,
413 for (; i < count; i++) {
414 me.markerGroup.getAt(i).hide(true);