Skip to content

Commit de217ae

Browse files
authored
Merge pull request #7825 from plotly/fix-7821-v4.0
Fix various issues with rendering `scattermap` icons
2 parents 25f6b02 + f34c663 commit de217ae

8 files changed

Lines changed: 60 additions & 10 deletions

File tree

draftlogs/7825_update.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Enable scattermap icons to render in color, upgrade Maki icons version to 8.2, and standardize scattermap legend icons to circles [[#7825](https://github.com/plotly/plotly.js/pull/7825)]

src/components/drawing/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1866,7 +1866,10 @@ function getMarkerAngle(d, trace) {
18661866

18671867
if (angle === undefined) {
18681868
angle = trace.marker.angle;
1869-
if (!angle || Lib.isArrayOrTypedArray(angle)) {
1869+
// For scattermap traces, `trace.marker.angle` defaults to 'auto',
1870+
// which is meaningful for the MapLibre code but not for plotly.js itself.
1871+
// Therefore we need to coerce any non-numeric values to 0 (no rotation).
1872+
if (!isNumeric(angle) || Lib.isArrayOrTypedArray(angle)) {
18701873
angle = 0;
18711874
}
18721875
}

src/components/legend/style.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,12 @@ module.exports = function style(s, gd, legend) {
207207

208208
if (showMarker) {
209209
dEdit.mc = boundVal('marker.color', pickFirst);
210-
dEdit.mx = boundVal('marker.symbol', pickFirst);
210+
// Scattermap traces use marker.symbol to specify the Maki icon used in
211+
// the map itself, which usually doesn't correspond to a valid
212+
// Plotly symbol. Always draw a circle so the swatch is consistent
213+
// across symbols rather than silently mismatched.
214+
var isScattermapTrace = trace.type === 'scattermap' || trace.type === 'scattermapbox';
215+
dEdit.mx = isScattermapTrace ? 'circle' : boundVal('marker.symbol', pickFirst);
211216
dEdit.mo = boundVal('marker.opacity', Lib.mean, [0.2, 1]);
212217
dEdit.mlc = boundVal('marker.line.color', pickFirst);
213218
dEdit.mlw = boundVal('marker.line.width', Lib.mean, [0, 5], CST_MARKER_LINE_WIDTH);

src/plots/map/layers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ function convertOpts(opts) {
230230
var textOpts = convertTextOpts(symbol.textposition, symbol.iconsize);
231231

232232
Lib.extendFlat(layout, {
233-
'icon-image': symbol.icon + '-15',
233+
'icon-image': symbol.icon,
234234
'icon-size': symbol.iconsize / 10,
235235

236236
'text-field': symbol.text,

src/plots/map/map.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
107107
var requestedIcons = {};
108108
map.on('styleimagemissing', function(e) {
109109
var id = e.id;
110-
if(!requestedIcons[id] && id.includes('-15')) {
110+
if(!requestedIcons[id] && /^[a-zA-Z0-9-]+$/.test(id)) {
111111
requestedIcons[id] = true;
112112
var img = new Image(15, 15);
113113
img.onload = function() {
114-
map.addImage(id, img);
114+
map.addImage(id, img, {sdf: true});
115115
};
116116
img.crossOrigin = 'Anonymous';
117-
img.src = 'https://unpkg.com/maki@2.1.0/icons/' + id + '.svg';
117+
img.src = "https://cdn.jsdelivr.net/npm/@mapbox/maki@8.2.0/icons/" + id + '.svg';
118118
}
119119
});
120120

src/traces/scattermap/convert.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ module.exports = function convert(gd, calcTrace) {
116116

117117
Lib.extendFlat(symbol.layout, {
118118
visibility: 'visible',
119-
'icon-image': '{symbol}-15',
119+
'icon-image': '{symbol}',
120120
'text-field': '{text}'
121121
});
122122

@@ -141,9 +141,11 @@ module.exports = function convert(gd, calcTrace) {
141141

142142
Lib.extendFlat(symbol.paint, {
143143
'icon-opacity': trace.opacity * trace.marker.opacity,
144-
145-
// TODO does not work ??
146-
'icon-color': trace.marker.color
144+
'icon-color': trace.marker.color,
145+
// Set a tiny blur to make the edges look nicer
146+
'icon-halo-color': trace.marker.color,
147+
'icon-halo-width': 2,
148+
'icon-halo-blur': 1.5,
147149
});
148150
}
149151

-1.38 KB
Loading

test/jasmine/tests/scattermap_test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,6 +1248,45 @@ describe('Test plotly events on a scattermap plot when css transform is present:
12481248
});
12491249
});
12501250

1251+
describe('scattermap legend', function() {
1252+
var gd;
1253+
1254+
beforeEach(function() {
1255+
Plotly.setPlotConfig({});
1256+
gd = createGraphDiv();
1257+
});
1258+
1259+
afterEach(function() {
1260+
Plotly.purge(gd);
1261+
destroyGraphDiv();
1262+
});
1263+
1264+
it('@gl should always draw a circle swatch regardless of marker.symbol', function(done) {
1265+
// marker.symbol on map traces is a Maki/sprite icon name that the SVG
1266+
// legend can't reproduce, so the swatch should consistently be a circle.
1267+
// The circle symbol path is the only one using an arc ('A') command;
1268+
// square/triangle/etc. use only straight (H/V/L) segments.
1269+
Plotly.newPlot(gd, {
1270+
data: [
1271+
{ type: 'scattermap', lat: [0], lon: [0], mode: 'markers', name: 'square', marker: { symbol: 'square' } },
1272+
{ type: 'scattermap', lat: [1], lon: [1], mode: 'markers', name: 'tri', marker: { symbol: 'triangle-stroked' } }
1273+
],
1274+
layout: {
1275+
map: { style: 'open-street-map', zoom: 6, center: { lat: 0.5, lon: 0.5 } },
1276+
showlegend: true
1277+
}
1278+
}).then(function() {
1279+
var swatches = gd.querySelectorAll('.legend .legendpoints path.scatterpts');
1280+
expect(swatches.length).toBe(2, 'one swatch per trace');
1281+
swatches.forEach(function(node) {
1282+
var d = node.getAttribute('d');
1283+
expect(d.indexOf('A')).toBeGreaterThan(-1, 'swatch is a circle (has arc): ' + d);
1284+
expect(d.indexOf('NaN')).toBe(-1, 'swatch path has no NaN: ' + d);
1285+
});
1286+
}).then(done, done.fail);
1287+
});
1288+
});
1289+
12511290
describe('scattermap restyle', function() {
12521291
var gd;
12531292

0 commit comments

Comments
 (0)