diff --git a/src/components/colorbar/draw.js b/src/components/colorbar/draw.js index 142e8e680c9..d8f8313c7df 100644 --- a/src/components/colorbar/draw.js +++ b/src/components/colorbar/draw.js @@ -434,7 +434,6 @@ module.exports = function draw(gd, id) { var vals = Axes.calcTicks(cbAxisOut); var transFn = Axes.makeTransFn(cbAxisOut); - var labelFns = Axes.makeLabelFns(cbAxisOut, shift); var tickSign = Axes.getTickSigns(cbAxisOut)[2]; Axes.drawTicks(gd, cbAxisOut, { @@ -448,9 +447,7 @@ module.exports = function draw(gd, id) { vals: vals, layer: axisLayer, transFn: transFn, - labelXFn: labelFns.labelXFn, - labelYFn: labelFns.labelYFn, - labelAnchorFn: labelFns.labelAnchorFn + labelFns: Axes.makeLabelFns(cbAxisOut, shift) }); }, function() { diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index e7c2f310473..d13d3f6104d 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -1802,14 +1802,11 @@ axes.drawOne = function(gd, ax, opts) { // TODO: mirror labels, esp for subplots seq.push(function() { - var labelFns = axes.makeLabelFns(ax, mainLinePosition); return axes.drawLabels(gd, ax, { vals: vals, layer: mainAxLayer, transFn: transFn, - labelXFn: labelFns.labelXFn, - labelYFn: labelFns.labelYFn, - labelAnchorFn: labelFns.labelAnchorFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition) }); }); @@ -1821,8 +1818,6 @@ axes.drawOne = function(gd, ax, opts) { seq.push(function() { labelLength += getLabelLevelSpan(ax, axId + 'tick') + pad; labelLength += ax._tickAngles[axId + 'tick'] ? ax.tickfont.size * LINE_SPACING : 0; - var secondaryPosition = mainLinePosition + labelLength * sgn; - var secondaryLabelFns = axes.makeLabelFns(ax, secondaryPosition); return axes.drawLabels(gd, ax, { vals: getSecondaryLabelVals(ax, vals), @@ -1831,9 +1826,7 @@ axes.drawOne = function(gd, ax, opts) { repositionOnUpdate: true, secondary: true, transFn: transFn, - labelXFn: secondaryLabelFns.labelXFn, - labelYFn: secondaryLabelFns.labelYFn, - labelAnchorFn: secondaryLabelFns.labelAnchorFn, + labelFns: axes.makeLabelFns(ax, mainLinePosition + labelLength * sgn) }); }); @@ -2178,66 +2171,79 @@ axes.makeTickPath = function(ax, shift, sgn, len) { * @param {number} shift * @param {number} angle [in degrees] ... * @return {object} - * - {fn} labelXFn - * - {fn} labelYFn - * - {fn} labelAnchorFn - * - {number} labelStandoff - * - {number} labelShift + * - {fn} xFn + * - {fn} yFn + * - {fn} anchorFn + * - {fn} heightFn + * - {number} labelStandoff (gap parallel to ticks) + * - {number} labelShift (gap perpendicular to ticks) */ axes.makeLabelFns = function(ax, shift, angle) { var axLetter = ax._id.charAt(0); - var pad = (ax.linewidth || 1) / 2; var ticksOnOutsideLabels = ax.tickson !== 'boundaries' && ax.ticks === 'outside'; - var labelStandoff = ticksOnOutsideLabels ? ax.ticklen : 0; + var labelStandoff = 0; var labelShift = 0; + if(ticksOnOutsideLabels) { + labelStandoff += ax.ticklen; + } if(angle && ax.ticks === 'outside') { var rad = Lib.deg2rad(angle); labelStandoff = ax.ticklen * Math.cos(rad) + 1; labelShift = ax.ticklen * Math.sin(rad); } - if(ax.showticklabels && (ticksOnOutsideLabels || ax.showline)) { labelStandoff += 0.2 * ax.tickfont.size; } + labelStandoff += (ax.linewidth || 1) / 2; - // Used in polar angular label x/y functions - // TODO generalize makeLabelFns so that it just work for angular axes var out = { labelStandoff: labelStandoff, labelShift: labelShift }; var x0, y0, ff, flipIt; + if(axLetter === 'x') { flipIt = ax.side === 'bottom' ? 1 : -1; x0 = labelShift * flipIt; - y0 = shift + (labelStandoff + pad) * flipIt; + y0 = shift + labelStandoff * flipIt; ff = ax.side === 'bottom' ? 1 : -0.2; - out.labelXFn = function(d) { return d.dx + x0; }; - out.labelYFn = function(d) { return d.dy + y0 + d.fontSize * ff; }; - out.labelAnchorFn = function(a) { + out.xFn = function(d) { return d.dx + x0; }; + out.yFn = function(d) { return d.dy + y0 + d.fontSize * ff; }; + out.anchorFn = function(d, a) { if(!isNumeric(a) || a === 0 || a === 180) { return 'middle'; } return (a * flipIt < 0) ? 'end' : 'start'; }; + out.heightFn = function(d, a, h) { + return (a < -60 || a > 60) ? -0.5 * h : + ax.side === 'top' ? -h : + 0; + }; } else if(axLetter === 'y') { flipIt = ax.side === 'right' ? 1 : -1; - x0 = labelStandoff + pad; + x0 = labelStandoff; y0 = -labelShift * flipIt; ff = Math.abs(ax.tickangle) === 90 ? 0.5 : 0; - out.labelXFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; }; - out.labelYFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; }; - out.labelAnchorFn = function(a) { + out.xFn = function(d) { return d.dx + shift + (x0 + d.fontSize * ff) * flipIt; }; + out.yFn = function(d) { return d.dy + y0 + d.fontSize * MID_SHIFT; }; + out.anchorFn = function(d, a) { if(isNumeric(a) && Math.abs(a) === 90) { return 'middle'; } return ax.side === 'right' ? 'start' : 'end'; }; + out.heightFn = function(d, a, h) { + a *= ax.side === 'left' ? 1 : -1; + return a < -30 ? -h : + a < 30 ? -0.5 * h : + 0; + }; } return out; @@ -2412,9 +2418,11 @@ axes.drawZeroLine = function(gd, ax, opts) { * - {boolean} repositionOnUpdate (set to true to reposition update selection) * - {boolean} secondary * - {fn} transFn - * - {fn} labelXFn - * - {fn} labelYFn - * - {fn} labelAnchorFn + * - {object} labelFns + * + {fn} xFn + * + {fn} yFn + * + {fn} anchorFn + * + {fn} heightFn */ axes.drawLabels = function(gd, ax, opts) { opts = opts || {}; @@ -2423,9 +2431,7 @@ axes.drawLabels = function(gd, ax, opts) { var axLetter = axId.charAt(0); var cls = opts.cls || axId + 'tick'; var vals = opts.vals; - var labelXFn = opts.labelXFn; - var labelYFn = opts.labelYFn; - var labelAnchorFn = opts.labelAnchorFn; + var labelFns = opts.labelFns; var tickAngle = opts.secondary ? 0 : ax.tickangle; var lastAngle = (ax._tickAngles || {})[cls]; @@ -2445,7 +2451,7 @@ axes.drawLabels = function(gd, ax, opts) { var newPromise = gd._promises.length; thisLabel - .call(svgTextUtils.positionText, labelXFn(d), labelYFn(d)) + .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d)) .call(Drawing.font, d.font, d.fontSize, d.fontColor) .text(d.text) .call(svgTextUtils.convertToTspans, gd); @@ -2469,47 +2475,26 @@ axes.drawLabels = function(gd, ax, opts) { if(opts.repositionOnUpdate) { tickLabels.each(function(d) { d3.select(this).select('text') - .call(svgTextUtils.positionText, labelXFn(d), labelYFn(d)); + .call(svgTextUtils.positionText, labelFns.xFn(d), labelFns.yFn(d)); }); } - // How much to shift a multi-line label to center it vertically. - function getAnchorHeight(lineCount, lineHeight, angle) { - var h = (lineCount - 1) * lineHeight; - if(axLetter === 'x') { - if(angle < -60 || 60 < angle) { - return -0.5 * h; - } else if(ax.side === 'top') { - return -h; - } - } else { - angle *= ax.side === 'left' ? 1 : -1; - if(angle < -30) { - return -h; - } else if(angle < 30) { - return -0.5 * h; - } - } - return 0; - } - function positionLabels(s, angle) { s.each(function(d) { var thisLabel = d3.select(this); var mathjaxGroup = thisLabel.select('.text-math-group'); - var anchor = labelAnchorFn(angle, d); + var anchor = labelFns.anchorFn(d, angle); var transform = opts.transFn.call(thisLabel.node(), d) + ((isNumeric(angle) && +angle !== 0) ? - (' rotate(' + angle + ',' + labelXFn(d) + ',' + - (labelYFn(d) - d.fontSize / 2) + ')') : + (' rotate(' + angle + ',' + labelFns.xFn(d) + ',' + + (labelFns.yFn(d) - d.fontSize / 2) + ')') : ''); - var anchorHeight = getAnchorHeight( - svgTextUtils.lineCount(thisLabel), - LINE_SPACING * d.fontSize, - isNumeric(angle) ? +angle : 0 - ); + // how much to shift a multi-line label to center it vertically. + var nLines = svgTextUtils.lineCount(thisLabel); + var lineHeight = LINE_SPACING * d.fontSize; + var anchorHeight = labelFns.heightFn(d, isNumeric(angle) ? +angle : 0, (nLines - 1) * lineHeight); if(anchorHeight) { transform += ' translate(0, ' + anchorHeight + ')'; diff --git a/src/plots/polar/polar.js b/src/plots/polar/polar.js index 937456ccc4a..f37772d2086 100644 --- a/src/plots/polar/polar.js +++ b/src/plots/polar/polar.js @@ -404,7 +404,6 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) { var vals = Axes.calcTicks(ax); var valsClipped = Axes.clipEnds(ax, vals); - var labelFns = Axes.makeLabelFns(ax, 0); var tickSign = Axes.getTickSigns(ax)[2]; Axes.drawTicks(gd, ax, { @@ -427,9 +426,7 @@ proto.updateRadialAxis = function(fullLayout, polarLayout) { vals: vals, layer: layers['radial-axis'], transFn: transFn, - labelXFn: labelFns.labelXFn, - labelYFn: labelFns.labelYFn, - labelAnchorFn: labelFns.labelAnchorFn + labelFns: Axes.makeLabelFns(ax, 0) }); } @@ -556,36 +553,31 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) { var out = Axes.makeLabelFns(ax, 0); var labelStandoff = out.labelStandoff; - var labelShift = out.labelShift; - var offset4fontsize = (angularLayout.ticks !== 'outside' ? 0.7 : 0.5); - var pad = (ax.linewidth || 1) / 2; + var labelFns = {}; - var labelXFn = function(d) { + labelFns.xFn = function(d) { var rad = t2g(d); - - var offset4tx = signSin(rad) === 0 ? - 0 : - Math.cos(rad) * (labelStandoff + pad + offset4fontsize * d.fontSize); - var offset4tick = signCos(rad) * (d.dx + labelStandoff + pad); - - return offset4tx + offset4tick; + return Math.cos(rad) * labelStandoff; }; - var labelYFn = function(d) { + labelFns.yFn = function(d) { var rad = t2g(d); + var ff = Math.sin(rad) > 0 ? 0.2 : 1; + return -Math.sin(rad) * (labelStandoff + d.fontSize * ff) + + Math.abs(Math.cos(rad)) * (d.fontSize * MID_SHIFT); + }; - var offset4tx = d.dy + d.fontSize * MID_SHIFT - labelShift; - var offset4tick = -Math.sin(rad) * (labelStandoff + pad + offset4fontsize * d.fontSize); - - return offset4tx + offset4tick; + labelFns.anchorFn = function(d) { + var rad = t2g(d); + var cos = Math.cos(rad); + return Math.abs(cos) < 0.1 ? + 'middle' : + (cos > 0 ? 'start' : 'end'); }; - // TODO maybe switch angle, d ordering ?? - var labelAnchorFn = function(angle, d) { + labelFns.heightFn = function(d, a, h) { var rad = t2g(d); - return signSin(rad) === 0 ? - (signCos(rad) > 0 ? 'start' : 'end') : - 'middle'; + return -0.5 * (1 + Math.sin(rad)) * h; }; var newTickLayout = strTickLayout(angularLayout); @@ -623,6 +615,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) { if(ax.visible) { var tickSign = ax.ticks === 'inside' ? -1 : 1; + var pad = (ax.linewidth || 1) / 2; Axes.drawTicks(gd, ax, { vals: vals, @@ -645,9 +638,7 @@ proto.updateAngularAxis = function(fullLayout, polarLayout) { layer: layers['angular-axis'], repositionOnUpdate: true, transFn: transFn, - labelXFn: labelXFn, - labelYFn: labelYFn, - labelAnchorFn: labelAnchorFn + labelFns: labelFns }); } @@ -1413,18 +1404,3 @@ function strTranslate(x, y) { function strRotate(angle) { return 'rotate(' + angle + ')'; } - -// because Math.sign(Math.cos(Math.PI / 2)) === 1 -// oh javascript ;) -function sign(v) { - return Math.abs(v) < 1e-10 ? 0 : - v > 0 ? 1 : -1; -} - -function signCos(v) { - return sign(Math.cos(v)); -} - -function signSin(v) { - return sign(Math.sin(v)); -} diff --git a/src/plots/ternary/ternary.js b/src/plots/ternary/ternary.js index e09c69dd7a4..f9ca0688efb 100644 --- a/src/plots/ternary/ternary.js +++ b/src/plots/ternary/ternary.js @@ -459,15 +459,11 @@ proto.drawAx = function(ax) { crisp: false }); - var labelFns = Axes.makeLabelFns(ax, 0, counterAngle); - Axes.drawLabels(gd, ax, { vals: vals, layer: axLayer, transFn: transFn, - labelXFn: labelFns.labelXFn, - labelYFn: labelFns.labelYFn, - labelAnchorFn: labelFns.labelAnchorFn + labelFns: Axes.makeLabelFns(ax, 0, counterAngle) }); }; diff --git a/test/image/baselines/glpolar_scatter.png b/test/image/baselines/glpolar_scatter.png index 36001048a0e..cc33d09d572 100644 Binary files a/test/image/baselines/glpolar_scatter.png and b/test/image/baselines/glpolar_scatter.png differ diff --git a/test/image/baselines/glpolar_style.png b/test/image/baselines/glpolar_style.png index cd7b776f669..b801bb42a4b 100644 Binary files a/test/image/baselines/glpolar_style.png and b/test/image/baselines/glpolar_style.png differ diff --git a/test/image/baselines/glpolar_subplots.png b/test/image/baselines/glpolar_subplots.png index 645d2858869..5da2a6d6709 100644 Binary files a/test/image/baselines/glpolar_subplots.png and b/test/image/baselines/glpolar_subplots.png differ diff --git a/test/image/baselines/layout_metatext.png b/test/image/baselines/layout_metatext.png index e8deb7bc512..d36837bec6f 100644 Binary files a/test/image/baselines/layout_metatext.png and b/test/image/baselines/layout_metatext.png differ diff --git a/test/image/baselines/polar_bar-overlay.png b/test/image/baselines/polar_bar-overlay.png index c10f093cd84..ee82a4e6150 100644 Binary files a/test/image/baselines/polar_bar-overlay.png and b/test/image/baselines/polar_bar-overlay.png differ diff --git a/test/image/baselines/polar_bar-stacked.png b/test/image/baselines/polar_bar-stacked.png index e317800d5fb..e40a4d8c769 100644 Binary files a/test/image/baselines/polar_bar-stacked.png and b/test/image/baselines/polar_bar-stacked.png differ diff --git a/test/image/baselines/polar_bar-width-base-offset.png b/test/image/baselines/polar_bar-width-base-offset.png index eac19c97c6c..c475f78e130 100644 Binary files a/test/image/baselines/polar_bar-width-base-offset.png and b/test/image/baselines/polar_bar-width-base-offset.png differ diff --git a/test/image/baselines/polar_blank.png b/test/image/baselines/polar_blank.png index 78d0dca28ea..eb443cf4413 100644 Binary files a/test/image/baselines/polar_blank.png and b/test/image/baselines/polar_blank.png differ diff --git a/test/image/baselines/polar_categories.png b/test/image/baselines/polar_categories.png index 67e45a62d91..98ac47fc481 100644 Binary files a/test/image/baselines/polar_categories.png and b/test/image/baselines/polar_categories.png differ diff --git a/test/image/baselines/polar_dates.png b/test/image/baselines/polar_dates.png index b102e5ea70c..44550e91f01 100644 Binary files a/test/image/baselines/polar_dates.png and b/test/image/baselines/polar_dates.png differ diff --git a/test/image/baselines/polar_direction.png b/test/image/baselines/polar_direction.png index 4b57bf4a130..48c9e9eff40 100644 Binary files a/test/image/baselines/polar_direction.png and b/test/image/baselines/polar_direction.png differ diff --git a/test/image/baselines/polar_fills.png b/test/image/baselines/polar_fills.png index fd34fb02040..b5bae8bda39 100644 Binary files a/test/image/baselines/polar_fills.png and b/test/image/baselines/polar_fills.png differ diff --git a/test/image/baselines/polar_funky-bars.png b/test/image/baselines/polar_funky-bars.png index f986930a388..6bf935a7fd4 100644 Binary files a/test/image/baselines/polar_funky-bars.png and b/test/image/baselines/polar_funky-bars.png differ diff --git a/test/image/baselines/polar_hole.png b/test/image/baselines/polar_hole.png index b701371fe93..bb85288c464 100644 Binary files a/test/image/baselines/polar_hole.png and b/test/image/baselines/polar_hole.png differ diff --git a/test/image/baselines/polar_line.png b/test/image/baselines/polar_line.png index bd00514df84..21a44316071 100644 Binary files a/test/image/baselines/polar_line.png and b/test/image/baselines/polar_line.png differ diff --git a/test/image/baselines/polar_long-category-angular-labels.png b/test/image/baselines/polar_long-category-angular-labels.png new file mode 100644 index 00000000000..0c41cac2fd8 Binary files /dev/null and b/test/image/baselines/polar_long-category-angular-labels.png differ diff --git a/test/image/baselines/polar_polygon-bars.png b/test/image/baselines/polar_polygon-bars.png index 892e5dd4ac5..970a1f198a7 100644 Binary files a/test/image/baselines/polar_polygon-bars.png and b/test/image/baselines/polar_polygon-bars.png differ diff --git a/test/image/baselines/polar_polygon-grids.png b/test/image/baselines/polar_polygon-grids.png index dea0454b4d3..0e6edffef8a 100644 Binary files a/test/image/baselines/polar_polygon-grids.png and b/test/image/baselines/polar_polygon-grids.png differ diff --git a/test/image/baselines/polar_r0dr-theta0dtheta.png b/test/image/baselines/polar_r0dr-theta0dtheta.png index b41f8c9497f..cb346963beb 100644 Binary files a/test/image/baselines/polar_r0dr-theta0dtheta.png and b/test/image/baselines/polar_r0dr-theta0dtheta.png differ diff --git a/test/image/baselines/polar_radial-range.png b/test/image/baselines/polar_radial-range.png index 8e6e570dc3e..53d424c1b28 100644 Binary files a/test/image/baselines/polar_radial-range.png and b/test/image/baselines/polar_radial-range.png differ diff --git a/test/image/baselines/polar_scatter.png b/test/image/baselines/polar_scatter.png index c5fffaf3d30..6213a188486 100644 Binary files a/test/image/baselines/polar_scatter.png and b/test/image/baselines/polar_scatter.png differ diff --git a/test/image/baselines/polar_sector.png b/test/image/baselines/polar_sector.png index d81ffb8f7c9..d06c4ff01e3 100644 Binary files a/test/image/baselines/polar_sector.png and b/test/image/baselines/polar_sector.png differ diff --git a/test/image/baselines/polar_subplots.png b/test/image/baselines/polar_subplots.png index e441914242d..88f447dc82c 100644 Binary files a/test/image/baselines/polar_subplots.png and b/test/image/baselines/polar_subplots.png differ diff --git a/test/image/baselines/polar_template.png b/test/image/baselines/polar_template.png index 98bea716231..2888477baa7 100644 Binary files a/test/image/baselines/polar_template.png and b/test/image/baselines/polar_template.png differ diff --git a/test/image/baselines/polar_ticks.png b/test/image/baselines/polar_ticks.png index 6a4eb5b642b..5dbdedc6733 100644 Binary files a/test/image/baselines/polar_ticks.png and b/test/image/baselines/polar_ticks.png differ diff --git a/test/image/baselines/polar_transforms.png b/test/image/baselines/polar_transforms.png index 9decc6694bd..e4ec9665e9b 100644 Binary files a/test/image/baselines/polar_transforms.png and b/test/image/baselines/polar_transforms.png differ diff --git a/test/image/baselines/polar_wind-rose.png b/test/image/baselines/polar_wind-rose.png index 951dfe8f833..f620514b359 100644 Binary files a/test/image/baselines/polar_wind-rose.png and b/test/image/baselines/polar_wind-rose.png differ diff --git a/test/image/mocks/polar_long-category-angular-labels.json b/test/image/mocks/polar_long-category-angular-labels.json new file mode 100644 index 00000000000..020ed5f9d28 --- /dev/null +++ b/test/image/mocks/polar_long-category-angular-labels.json @@ -0,0 +1,84 @@ +{ + "data": [ + { + "r": [0.97, 0.97, 0.75, 0.83, 1, 1, 0.45, 0.97, 1, 0, 0.01, 0.97 ], + "mode": "lines", + "theta": ["Abstracts", "Affiliations", "Award Numbers", "Funders", "Licenses", "Open References", "Orcids", "References", "Resource Links", "Similarity Checking", "Update Policies", "Abstracts"], + "line": { + "color": "orangered", + "width": 4 + }, + "type": "scatterpolar", + "name": "Backfile", + "legendgroup": "Backfile" + }, + { + "r": [0.98, 0.98, 0.83, 0.89, 1, 1, 0.99, 0.96, 1, 0, 0, 0.98], + "mode": "lines", + "theta": ["Abstracts", "Affiliations", "Award Numbers", "Funders", "Licenses", "Open References", "Orcids", "References", "Resource Links", "Similarity Checking", "Update Policies", "Abstracts"], + "line": { + "color": "blue", + "width": 2 + }, + "type": "scatterpolar", + "name": "Current", + "legendgroup": "Current" + }, + { + "r": [0.97, 0.97, 0.75, 0.83, 1, 1, 0.45, 0.97, 1, 0, 0.01, 0.97 ], + "mode": "lines", + "theta": ["Abstracts
Abstracts", "Affiliations
Affiliations", "Award
Numbers", "Funders
Funders", "Licenses
Licenses", "Open
References", "Orcids
Orcids
Orcids", "References
References", "Resource
Links", "Similarity
Checking", "Update
Policies", "Abstracts
Abstracts"], + "line": { + "color": "orangered", + "width": 4 + }, + "type": "scatterpolar", + "name": "Backfile", + "legendgroup": "Backfile", + "showlegend": false, + "subplot": "polar2" + }, + { + "r": [0.98, 0.98, 0.83, 0.89, 1, 1, 0.99, 0.96, 1, 0, 0, 0.98], + "mode": "lines", + "theta": ["Abstracts
Abstracts", "Affiliations
Affiliations", "Award
Numbers", "Funders
Funders", "Licenses
Licenses", "Open
References", "Orcids
Orcids
Orcids", "References
References", "Resource
Links", "Similarity
Checking", "Update
Policies", "Abstracts
Abstracts"], + "line": { + "color": "blue", + "width": 2 + }, + "type": "scatterpolar", + "name": "Current", + "legendgroup": "Current", + "showlegend": false, + "subplot": "polar2" + } + ], + "layout": { + "grid": {"rows": 2, "columns": 1, "ygap": 0.15}, + "height": 800, + "polar": { + "domain": {"row": 0, "column": 0}, + "angularaxis": { + "rotation": 90, + "direction": "clockwise" + } + }, + "polar2": { + "domain": {"row": 1, "column": 0}, + "angularaxis": { + "rotation": 90, + "direction": "clockwise" + } + }, + "legend": { + "y": 0, + "yanchor": "top", + "x": 0, + "xanchor": "left", + "tracegroupgap": 0 + }, + "title": { + "text": "eLife Sciences Publications
Ltd: journal-article (8.62 - 7.95 = 0.67)" + } + } +}