From 54ae088f6d7492b8073dd597b2e242df3e67ee80 Mon Sep 17 00:00:00 2001 From: "dhruvgarg.garg123@gmail.com" Date: Tue, 17 Feb 2026 05:54:21 +0000 Subject: [PATCH 1/6] feat(cartesian): add symmetrical log axis scale (symlog) --- src/plots/cartesian/layout_attributes.js | 6 ++- src/plots/cartesian/set_convert.js | 53 ++++++++++++++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index 25b60cfa28e..38fcbd4e60a 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -235,7 +235,7 @@ module.exports = { // '-' means we haven't yet run autotype or couldn't find any data // it gets turned into linear in gd._fullLayout but not copied back // to gd.data like the others are. - values: ['-', 'linear', 'log', 'date', 'category', 'multicategory'], + values: ['-', 'linear', 'log', 'symlog', 'date', 'category', 'multicategory'], dflt: '-', editType: 'calc', // we forget when an axis has been autotyped, just writing the auto @@ -247,7 +247,9 @@ module.exports = { 'Sets the axis type.', 'By default, plotly attempts to determined the axis type', 'by looking into the data of the traces that referenced', - 'the axis in question.' + 'the axis in question.', + 'With *symlog*, the axis is log-scaled with a linear range', + 'around zero.' ].join(' ') }, autotypenumbers: { diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index a51ff198456..f55342b1ba9 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -78,6 +78,16 @@ module.exports = function setConvert(ax, fullLayout) { } else return BADNUM; } + function toSymlog(v) { + // Using asinh as a smooth approximation to symlog + // TODO: Make the linear threshold configurable? Currently implicit 1. + return Math.asinh(v) / Math.LN10; + } + + function fromSymlog(v) { + return Math.sinh(v * Math.LN10); + } + /* * wrapped dateTime2ms that: * - accepts ms numbers for backward compatibility @@ -241,14 +251,22 @@ module.exports = function setConvert(ax, fullLayout) { } // conversions among c/l/p are fairly simple - do them together for all axis types - ax.c2l = (ax.type === 'log') ? toLog : ensureNumber; - ax.l2c = (ax.type === 'log') ? fromLog : ensureNumber; - - ax.l2p = l2p; - ax.p2l = p2l; - - ax.c2p = (ax.type === 'log') ? function(v, clip) { return l2p(toLog(v, clip)); } : l2p; - ax.p2c = (ax.type === 'log') ? function(px) { return fromLog(p2l(px)); } : p2l; + if(ax.type === 'log') { + ax.c2l = toLog; + ax.l2c = fromLog; + ax.c2p = function(v, clip) { return l2p(toLog(v, clip)); }; + ax.p2c = function(px) { return fromLog(p2l(px)); }; + } else if(ax.type === 'symlog') { + ax.c2l = toSymlog; + ax.l2c = fromSymlog; + ax.c2p = function(v) { return l2p(toSymlog(v)); }; + ax.p2c = function(px) { return fromSymlog(p2l(px)); }; + } else { + ax.c2l = ensureNumber; + ax.l2c = ensureNumber; + ax.c2p = l2p; + ax.p2c = p2l; + } /* * now type-specific conversions for **ALL** other combinations @@ -281,6 +299,25 @@ module.exports = function setConvert(ax, fullLayout) { ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); }; ax.p2r = p2l; + ax.cleanPos = ensureNumber; + } else if(ax.type === 'symlog') { + // Symlog implementation using arcsinh + // d and c are data vals, r and l are transformed (symlogged) + ax.d2r = ax.d2l = function(v) { return toSymlog(cleanNumber(v)); }; + ax.r2d = ax.r2c = function(v) { return fromSymlog(cleanNumber(v)); }; + + ax.d2c = ax.r2l = cleanNumber; + ax.c2d = ax.l2r = ensureNumber; + + ax.c2r = toSymlog; + ax.l2d = fromSymlog; + + ax.d2p = function(v) { return ax.l2p(ax.d2r(v)); }; + ax.p2d = function(px) { return fromSymlog(p2l(px)); }; + + ax.r2p = function(v) { return ax.l2p(cleanNumber(v)); }; + ax.p2r = p2l; + ax.cleanPos = ensureNumber; } else if(ax.type === 'date') { // r and d are date strings, l and c are ms From 3fcfb76ae183ec41358174b932b752d449b3fb97 Mon Sep 17 00:00:00 2001 From: "dhruvgarg.garg123@gmail.com" Date: Tue, 17 Feb 2026 05:54:39 +0000 Subject: [PATCH 2/6] fix(map): auto-calculate zoom and center when not provided --- src/plots/map/layout_defaults.js | 120 ++++++++++++++++++++++++++++++- 1 file changed, 118 insertions(+), 2 deletions(-) diff --git a/src/plots/map/layout_defaults.js b/src/plots/map/layout_defaults.js index 5cb2531fa1c..46225303624 100644 --- a/src/plots/map/layout_defaults.js +++ b/src/plots/map/layout_defaults.js @@ -12,11 +12,12 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { type: 'map', attributes: layoutAttributes, handleDefaults: handleDefaults, - partition: 'y' + partition: 'y', + fullData: fullData }); }; -function handleDefaults(containerIn, containerOut, coerce) { +function handleDefaults(containerIn, containerOut, coerce, opts) { coerce('style'); coerce('center.lon'); coerce('center.lat'); @@ -28,6 +29,115 @@ function handleDefaults(containerIn, containerOut, coerce) { var east = coerce('bounds.east'); var south = coerce('bounds.south'); var north = coerce('bounds.north'); + + // Auto-calculate bounds from data if not provided + if(west === undefined && east === undefined && south === undefined && north === undefined) { + var traceBounds = { + west: Infinity, + east: -Infinity, + south: Infinity, + north: -Infinity + }; + var hasTraceBounds = false; + + for(var i = 0; i < fullData.length; i++) { + var trace = fullData[i]; + // Check if trace belongs to this map subplot + if(trace.visible !== true || trace.subplot !== opts.id) continue; + + // Handle scattermap traces + if(trace.type === 'scattermap') { + var lon = trace.lon || []; + var lat = trace.lat || []; + var len = Math.min(lon.length, lat.length); + + for(var j = 0; j < len; j++) { + var l = lon[j]; + var t = lat[j]; + if(l !== undefined && t !== undefined) { + traceBounds.west = Math.min(traceBounds.west, l); + traceBounds.east = Math.max(traceBounds.east, l); + traceBounds.south = Math.min(traceBounds.south, t); + traceBounds.north = Math.max(traceBounds.north, t); + hasTraceBounds = true; + } + } + } + // Add other map trace types here if needed (choroplethmap, etc) + } + + if(hasTraceBounds) { + var domain = containerOut.domain; + var width = layoutOut.width * (domain.x[1] - domain.x[0]); + var height = layoutOut.height * (domain.y[1] - domain.y[0]); + + // Default zoom calculation + if(containerOut.zoom === undefined) { + var padding = 0.1; // 10% padding + var dLon = Math.abs(traceBounds.east - traceBounds.west); + if(dLon === 0) dLon = 0.01; // Avoid division by zero + if(dLon > 360) dLon = 360; + + // Simple Mercator projection for lat + function mercatorY(lat) { + var rad = lat * Math.PI / 180; + return Math.log(Math.tan(Math.PI / 4 + rad / 2)); + } + + var yNorth = mercatorY(traceBounds.north); + var ySouth = mercatorY(traceBounds.south); + var dLat = Math.abs(yNorth - ySouth); + if(dLat === 0) dLat = 0.01; + + // 360 degrees = 2*PI radians. Mapbox world width is 512 pixels at zoom 0? + // Wait, Mapbox tile size is 512px by default in GL JS. + // 360 degrees fits in 512px at z=0? + // Let's use 360 degrees fits in 256 * 2^z (standard web mercator) + // Mapbox GL usually matches this. + // But let's be conservative. + + // Zoom for longitude fit: + // pixelWidth = (dLon / 360) * 512 * 2^z + // 2^z = (pixelWidth * 360) / (dLon * 512) + // z = log2(...) + var zLon = Math.log2((width * 360) / (dLon * 512)); + + // Zoom for latitude fit: + // pixelHeight = (dLat / (2 * Math.PI)) * 512 * 2^z + // 2^z = (pixelHeight * 2 * Math.PI) / (dLat * 512) + var zLat = Math.log2((height * 2 * Math.PI) / (dLat * 512)); + + var zoom = Math.min(zLon, zLat); + zoom = Math.max(0, Math.min(22, zoom)); // Clamp to valid zoom range + zoom -= padding; // Apply padding + + containerOut.zoom = zoom; + } + + // Default center calculation + if(containerOut.center.lon === undefined) { + containerOut.center.lon = (traceBounds.west + traceBounds.east) / 2; + } + if(containerOut.center.lat === undefined) { + // Center latitude in Mercator projection, then unproject + function mercatorY(lat) { + var rad = lat * Math.PI / 180; + return Math.log(Math.tan(Math.PI / 4 + rad / 2)); + } + function unMercatorY(y) { + return (2 * Math.atan(Math.exp(y)) - Math.PI / 2) * 180 / Math.PI; + } + + var yNorth = mercatorY(traceBounds.north); + var ySouth = mercatorY(traceBounds.south); + var yCenter = (yNorth + ySouth) / 2; + containerOut.center.lat = unMercatorY(yCenter); + } + + // Do NOT set containerOut.bounds here as that restricts panning! + } + } // End of auto-calculate bounds block + if( west === undefined || east === undefined || @@ -35,6 +145,12 @@ function handleDefaults(containerIn, containerOut, coerce) { north === undefined ) { delete containerOut.bounds; + + if(fitBounds) { + delete containerOut.center.lon; + delete containerOut.center.lat; + delete containerOut.zoom; + } } handleArrayContainerDefaults(containerIn, containerOut, { From cc8d962ba843e87824bacbfb5ac64b4401d7163e Mon Sep 17 00:00:00 2001 From: "dhruvgarg.garg123@gmail.com" Date: Tue, 17 Feb 2026 05:54:56 +0000 Subject: [PATCH 3/6] feat(scattermap): add support for line style (dash) --- src/traces/scattermap/attributes.js | 8 ++++---- src/traces/scattermap/convert.js | 22 +++++++++++++++++++++- src/traces/scattermapbox/attributes.js | 8 ++++---- src/traces/scattermapbox/convert.js | 22 +++++++++++++++++++++- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/traces/scattermap/attributes.js b/src/traces/scattermap/attributes.js index 29c0cfcbb8a..985027ffac6 100644 --- a/src/traces/scattermap/attributes.js +++ b/src/traces/scattermap/attributes.js @@ -99,10 +99,10 @@ module.exports = overrideAll( line: { color: lineAttrs.color, - width: lineAttrs.width - - // TODO - // dash: dash + width: lineAttrs.width, + dash: extendFlat({}, scatterAttrs.line.dash, { + editType: 'plot' + }) }, connectgaps: scatterAttrs.connectgaps, diff --git a/src/traces/scattermap/convert.js b/src/traces/scattermap/convert.js index c2bfce0c56d..b22cc674674 100644 --- a/src/traces/scattermap/convert.js +++ b/src/traces/scattermap/convert.js @@ -69,7 +69,9 @@ module.exports = function convert(gd, calcTrace) { 'line-opacity': trace.opacity }); - // TODO convert line.dash into line-dasharray + if (trace.line.dash) { + line.paint['line-dasharray'] = convertDash(trace.line.dash, trace.line.width); + } } if (hasCircles) { @@ -197,6 +199,24 @@ function makeCircleOpts(calcTrace) { return s / 2; } + function convertDash(dash, width) { + var dashList; + if (dash === 'solid') dashList = []; + else if (dash === 'dot') dashList = [1, 2]; + else if (dash === 'dash') dashList = [4, 2]; + else if (dash === 'longdash') dashList = [8, 2]; + else if (dash === 'dashdot') dashList = [4, 2, 1, 2]; + else if (dash === 'longdashdot') dashList = [8, 2, 1, 2]; + else if (typeof dash === 'string') dashList = dash.split(/[\s,]+/).map(Number); + else dashList = dash; + + if (dashList.length) { + return dashList.map(function (v) { + return (v * width) / 2; + }); + } + } + var colorFn; if (arrayColor) { if (Colorscale.hasColorscale(trace, 'marker')) { diff --git a/src/traces/scattermapbox/attributes.js b/src/traces/scattermapbox/attributes.js index 2a41cea95cd..afc568ce1f2 100644 --- a/src/traces/scattermapbox/attributes.js +++ b/src/traces/scattermapbox/attributes.js @@ -99,10 +99,10 @@ module.exports = overrideAll( line: { color: lineAttrs.color, - width: lineAttrs.width - - // TODO - // dash: dash + width: lineAttrs.width, + dash: extendFlat({}, scatterAttrs.line.dash, { + editType: 'plot' + }) }, connectgaps: scatterAttrs.connectgaps, diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js index 380324bbefb..1844b0099f7 100644 --- a/src/traces/scattermapbox/convert.js +++ b/src/traces/scattermapbox/convert.js @@ -69,7 +69,9 @@ module.exports = function convert(gd, calcTrace) { 'line-opacity': trace.opacity }); - // TODO convert line.dash into line-dasharray + if (trace.line.dash) { + line.paint['line-dasharray'] = convertDash(trace.line.dash, trace.line.width); + } } if (hasCircles) { @@ -197,6 +199,24 @@ function makeCircleOpts(calcTrace) { return s / 2; } + function convertDash(dash, width) { + var dashList; + if (dash === 'solid') dashList = []; + else if (dash === 'dot') dashList = [1, 2]; + else if (dash === 'dash') dashList = [4, 2]; + else if (dash === 'longdash') dashList = [8, 2]; + else if (dash === 'dashdot') dashList = [4, 2, 1, 2]; + else if (dash === 'longdashdot') dashList = [8, 2, 1, 2]; + else if (typeof dash === 'string') dashList = dash.split(/[\s,]+/).map(Number); + else dashList = dash; + + if (dashList.length) { + return dashList.map(function (v) { + return (v * width) / 2; + }); + } + } + var colorFn; if (arrayColor) { if (Colorscale.hasColorscale(trace, 'marker')) { From dfe40b945fa8f7f38f7ffdb10074aed65682e45c Mon Sep 17 00:00:00 2001 From: "dhruvgarg.garg123@gmail.com" Date: Thu, 19 Feb 2026 06:08:31 +0000 Subject: [PATCH 4/6] feat: add symlog axis support --- src/plots/cartesian/axes.js | 2 +- src/plots/cartesian/set_convert.js | 3 +++ test/image/baselines/axes_symlog.png | Bin 0 -> 25394 bytes test/image/mocks/axes_symlog.json | 21 +++++++++++++++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 test/image/baselines/axes_symlog.png create mode 100644 test/image/mocks/axes_symlog.json diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 38a8a7a8909..b181832960c 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -4571,7 +4571,7 @@ function swapAxisGroup(gd, xIds, yIds) { var noSwapAttrs = [ 'anchor', 'domain', 'overlaying', 'position', 'side', 'tickangle', 'editType' ]; - var numericTypes = ['linear', 'log']; + var numericTypes = ['linear', 'log', 'symlog']; for(i = 0; i < allAxKeys.length; i++) { var keyi = allAxKeys[i]; diff --git a/src/plots/cartesian/set_convert.js b/src/plots/cartesian/set_convert.js index f55342b1ba9..868032d41cf 100644 --- a/src/plots/cartesian/set_convert.js +++ b/src/plots/cartesian/set_convert.js @@ -250,6 +250,9 @@ module.exports = function setConvert(ax, fullLayout) { }; } + ax.l2p = l2p; + ax.p2l = p2l; + // conversions among c/l/p are fairly simple - do them together for all axis types if(ax.type === 'log') { ax.c2l = toLog; diff --git a/test/image/baselines/axes_symlog.png b/test/image/baselines/axes_symlog.png new file mode 100644 index 0000000000000000000000000000000000000000..7ef6459aacd8f76dce50445b26412e165bc8b710 GIT binary patch literal 25394 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 literal 0 HcmV?d00001 diff --git a/test/image/mocks/axes_symlog.json b/test/image/mocks/axes_symlog.json new file mode 100644 index 00000000000..0a71958b4cb --- /dev/null +++ b/test/image/mocks/axes_symlog.json @@ -0,0 +1,21 @@ +{ + "data": [ + { + "x": [-1000, -100, -10, -1, -0.1, 0, 0.1, 1, 10, 100, 1000], + "y": [1000, 100, 10, 1, 0.1, 0, -0.1, -1, -10, -100, -1000], + "type": "scatter", + "mode": "lines+markers" + } + ], + "layout": { + "title": {"text": "Symlog axis example"}, + "xaxis": { + "type": "symlog", + "autorange": true + }, + "yaxis": { + "type": "symlog", + "autorange": true + } + } +} From 2b8d8ec34751ab5072ddffc94654b93d0faf9ad2 Mon Sep 17 00:00:00 2001 From: "dhruvgarg.garg123@gmail.com" Date: Thu, 19 Feb 2026 06:08:43 +0000 Subject: [PATCH 5/6] feat: add scattermapbox line dash --- src/traces/scattermapbox/convert.js | 36 ++++++++--------- src/traces/scattermapbox/defaults.js | 2 +- .../baselines/scattermapbox_line_dash.png | Bin 0 -> 12007 bytes test/image/mocks/scattermapbox_line_dash.json | 37 ++++++++++++++++++ 4 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 test/image/baselines/scattermapbox_line_dash.png create mode 100644 test/image/mocks/scattermapbox_line_dash.json diff --git a/src/traces/scattermapbox/convert.js b/src/traces/scattermapbox/convert.js index 1844b0099f7..352ee5e15a4 100644 --- a/src/traces/scattermapbox/convert.js +++ b/src/traces/scattermapbox/convert.js @@ -17,6 +17,24 @@ var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPo var NEWLINES = require('../../lib/svg_text_utils').NEWLINES; var BR_TAG_ALL = require('../../lib/svg_text_utils').BR_TAG_ALL; +function convertDash(dash, width) { + var dashList; + if (dash === 'solid') dashList = []; + else if (dash === 'dot') dashList = [1, 2]; + else if (dash === 'dash') dashList = [4, 2]; + else if (dash === 'longdash') dashList = [8, 2]; + else if (dash === 'dashdot') dashList = [4, 2, 1, 2]; + else if (dash === 'longdashdot') dashList = [8, 2, 1, 2]; + else if (typeof dash === 'string') dashList = dash.split(/[\s,]+/).map(Number); + else dashList = dash; + + if (dashList.length) { + return dashList.map(function (v) { + return (v * width) / 2; + }); + } +} + module.exports = function convert(gd, calcTrace) { var trace = calcTrace[0].trace; @@ -199,24 +217,6 @@ function makeCircleOpts(calcTrace) { return s / 2; } - function convertDash(dash, width) { - var dashList; - if (dash === 'solid') dashList = []; - else if (dash === 'dot') dashList = [1, 2]; - else if (dash === 'dash') dashList = [4, 2]; - else if (dash === 'longdash') dashList = [8, 2]; - else if (dash === 'dashdot') dashList = [4, 2, 1, 2]; - else if (dash === 'longdashdot') dashList = [8, 2, 1, 2]; - else if (typeof dash === 'string') dashList = dash.split(/[\s,]+/).map(Number); - else dashList = dash; - - if (dashList.length) { - return dashList.map(function (v) { - return (v * width) / 2; - }); - } - } - var colorFn; if (arrayColor) { if (Colorscale.hasColorscale(trace, 'marker')) { diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js index faeb083ac08..521d0807e7d 100644 --- a/src/traces/scattermapbox/defaults.js +++ b/src/traces/scattermapbox/defaults.js @@ -49,7 +49,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } if (subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, { noDash: true }); + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); coerce('connectgaps'); } diff --git a/test/image/baselines/scattermapbox_line_dash.png b/test/image/baselines/scattermapbox_line_dash.png new file mode 100644 index 0000000000000000000000000000000000000000..9ac1050ba2eda2052e94d5114896bbe0c00429f5 GIT binary patch literal 12007 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 W Date: Thu, 19 Feb 2026 15:29:05 +0000 Subject: [PATCH 6/6] fix(map): define fullData/layoutOut in map defaults --- src/plots/map/layout_defaults.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/plots/map/layout_defaults.js b/src/plots/map/layout_defaults.js index 46225303624..2c6466bd329 100644 --- a/src/plots/map/layout_defaults.js +++ b/src/plots/map/layout_defaults.js @@ -13,11 +13,14 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut, fullData) { attributes: layoutAttributes, handleDefaults: handleDefaults, partition: 'y', - fullData: fullData + fullData: fullData, + layoutOut: layoutOut }); }; function handleDefaults(containerIn, containerOut, coerce, opts) { + var fullData = opts.fullData || []; + var layoutOut = opts.layoutOut || {}; coerce('style'); coerce('center.lon'); coerce('center.lat'); @@ -145,12 +148,6 @@ function handleDefaults(containerIn, containerOut, coerce, opts) { north === undefined ) { delete containerOut.bounds; - - if(fitBounds) { - delete containerOut.center.lon; - delete containerOut.center.lat; - delete containerOut.zoom; - } } handleArrayContainerDefaults(containerIn, containerOut, {