Skip to content

Commit 7d3dd35

Browse files
committed
implement arrayOk for marker.opacity in scattermapbox traces
1 parent 746ec7e commit 7d3dd35

File tree

5 files changed

+108
-74
lines changed

5 files changed

+108
-74
lines changed

src/traces/scattermapbox/attributes.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,7 @@ module.exports = {
8282
'are only available for *circle* symbols.'
8383
].join(' ')
8484
},
85-
opacity: extendFlat({}, markerAttrs.opacity, {
86-
arrayOk: false
87-
}),
85+
opacity: markerAttrs.opacity,
8886
size: markerAttrs.size,
8987
sizeref: markerAttrs.sizeref,
9088
sizemin: markerAttrs.sizemin,

src/traces/scattermapbox/convert.js

+51-11
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
'use strict';
1111

12+
var isNumeric = require('fast-isnumeric');
13+
1214
var Lib = require('../../lib');
1315
var BADNUM = require('../../constants/numerical').BADNUM;
1416
var geoJsonUtils = require('../../lib/geojson_utils');
@@ -20,7 +22,7 @@ var convertTextOpts = require('../../plots/mapbox/convert_text_opts');
2022

2123
var COLOR_PROP = 'circle-color';
2224
var SIZE_PROP = 'circle-radius';
23-
25+
var OPACITY_PROP = 'circle-opacity';
2426

2527
module.exports = function convert(calcTrace) {
2628
var trace = calcTrace[0].trace;
@@ -80,12 +82,13 @@ module.exports = function convert(calcTrace) {
8082
var hash = {};
8183
hash[COLOR_PROP] = {};
8284
hash[SIZE_PROP] = {};
85+
hash[OPACITY_PROP] = {};
8386

8487
circle.geojson = makeCircleGeoJSON(calcTrace, hash);
8588
circle.layout.visibility = 'visible';
8689

8790
Lib.extendFlat(circle.paint, {
88-
'circle-opacity': trace.opacity * trace.marker.opacity,
91+
'circle-opacity': calcCircleOpacity(trace, hash),
8992
'circle-color': calcCircleColor(trace, hash),
9093
'circle-radius': calcCircleRadius(trace, hash)
9194
});
@@ -179,8 +182,18 @@ function makeCircleGeoJSON(calcTrace, hash) {
179182
var sizeFn;
180183
if(subTypes.isBubble(trace)) {
181184
sizeFn = makeBubbleSizeFn(trace);
182-
} else if(Array.isArray(marker.size)) {
183-
sizeFn = Lib.identity;
185+
}
186+
187+
function combineOpacities(d, mo) {
188+
return trace.opacity * mo;
189+
}
190+
191+
var opacityFn;
192+
if(Array.isArray(marker.opacity)) {
193+
opacityFn = function(d) {
194+
var mo = isNumeric(d.mo) ? +Lib.constrain(d.mo, 0, 1) : 0;
195+
return combineOpacities(d, mo);
196+
};
184197
}
185198

186199
// Translate vals in trace arrayOk containers
@@ -204,7 +217,12 @@ function makeCircleGeoJSON(calcTrace, hash) {
204217
var mcc = calcPt.mcc = colorFn(calcPt.mc);
205218
translate(props, COLOR_PROP, mcc, i);
206219
}
207-
if(sizeFn) translate(props, SIZE_PROP, sizeFn(calcPt.ms), i);
220+
if(sizeFn) {
221+
translate(props, SIZE_PROP, sizeFn(calcPt.ms), i);
222+
}
223+
if(opacityFn) {
224+
translate(props, OPACITY_PROP, opacityFn(calcPt), i);
225+
}
208226

209227
features.push({
210228
type: 'Feature',
@@ -304,14 +322,9 @@ function calcCircleRadius(trace, hash) {
304322
stops.push([ hash[SIZE_PROP][val], +val ]);
305323
}
306324

307-
// stops indices must be sorted
308-
stops.sort(function(a, b) {
309-
return a[0] - b[0];
310-
});
311-
312325
out = {
313326
property: SIZE_PROP,
314-
stops: stops
327+
stops: stops.sort(ascending)
315328
};
316329
}
317330
else {
@@ -321,6 +334,31 @@ function calcCircleRadius(trace, hash) {
321334
return out;
322335
}
323336

337+
function calcCircleOpacity(trace, hash) {
338+
var marker = trace.marker;
339+
var out;
340+
341+
if(Array.isArray(marker.opacity) || trace._hasDimmedPts) {
342+
var vals = Object.keys(hash[OPACITY_PROP]);
343+
var stops = [];
344+
345+
for(var i = 0; i < vals.length; i++) {
346+
var val = vals[i];
347+
stops.push([hash[OPACITY_PROP][val], +val]);
348+
}
349+
350+
out = {
351+
property: OPACITY_PROP,
352+
stops: stops.sort(ascending)
353+
};
354+
}
355+
else {
356+
out = trace.opacity * marker.opacity;
357+
}
358+
359+
return out;
360+
}
361+
324362
function getFillFunc(attr) {
325363
if(Array.isArray(attr)) {
326364
return function(v) { return v; };
@@ -335,6 +373,8 @@ function getFillFunc(attr) {
335373

336374
function blankFillFunc() { return ''; }
337375

376+
function ascending(a, b) { return a[0] - b[0]; }
377+
338378
// only need to check lon (OR lat)
339379
function isBADNUM(lonlat) {
340380
return lonlat[0] === BADNUM;
-13.6 KB
Loading

test/image/mocks/mapbox_bubbles.json

+17-59
Original file line numberDiff line numberDiff line change
@@ -2,72 +2,30 @@
22
"data": [
33
{
44
"type": "scattermapbox",
5-
"lon": [
6-
10,
7-
20,
8-
30
9-
],
10-
"lat": [
11-
10,
12-
20,
13-
30
14-
],
5+
"lon": [10, 20, 30],
6+
"lat": [10, 20, 30],
157
"marker": {
16-
"size": [
17-
20,
18-
10,
19-
40
20-
],
21-
"color": [
22-
"red",
23-
"blue",
24-
"orange"
25-
]
26-
}
8+
"size": [20, 10, 40],
9+
"color": ["red", "blue", "orange"],
10+
"opacity": [0.3, 0.5, 1]
11+
},
12+
"opacity": 0.7
2713
},
2814
{
2915
"type": "scattermapbox",
30-
"lon": [
31-
-75,
32-
-120,
33-
100
34-
],
35-
"lat": [
36-
45,
37-
20,
38-
-40
39-
],
16+
"lon": [-75, -120, 100],
17+
"lat": [45, 20, -40],
4018
"marker": {
41-
"size": [
42-
60,
43-
20,
44-
40
45-
],
46-
"color": [
47-
0,
48-
20,
49-
30
50-
],
19+
"size": [60, 20, 40],
20+
"color": [0, 20, 30],
5121
"colorbar": {},
5222
"cmin": 0,
5323
"cmax": 30,
5424
"colorscale": [
55-
[
56-
0,
57-
"rgb(220,220,220)"
58-
],
59-
[
60-
0.2,
61-
"rgb(245,195,157)"
62-
],
63-
[
64-
0.4,
65-
"rgb(245,160,105)"
66-
],
67-
[
68-
1,
69-
"rgb(178,10,28)"
70-
]
25+
[0, "rgb(220,220,220)"],
26+
[0.2, "rgb(245,195,157)"],
27+
[0.4, "rgb(245,160,105)"],
28+
[1, "rgb(178,10,28)"]
7129
]
7230
}
7331
}
@@ -79,7 +37,7 @@
7937
},
8038
"showlegend": false,
8139
"height": 450,
82-
"width": 1100,
83-
"autosize": true
40+
"width": 600,
41+
"margin": {"l": 10}
8442
}
8543
}

test/jasmine/tests/scattermapbox_test.js

+39-1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ describe('scattermapbox convert', function() {
155155
stops: [ [0, 5], [1, 10], [2, 0] ]
156156
}, 'circle-radius stops');
157157

158+
expect(opts.circle.paint['circle-opacity']).toBe(0.7, 'circle-opacity');
159+
158160
var circleProps = opts.circle.geojson.features.map(function(f) {
159161
return f.properties;
160162
});
@@ -169,6 +171,43 @@ describe('scattermapbox convert', function() {
169171
], 'geojson feature properties');
170172
});
171173

174+
it('should fill circle-opacity correctly', function() {
175+
var opts = _convert(Lib.extendFlat({}, base, {
176+
mode: 'markers',
177+
marker: {
178+
symbol: 'circle',
179+
size: 10,
180+
color: 'red',
181+
opacity: [1, null, 0.5, '0.5', '1', 0, 0.8]
182+
},
183+
opacity: 0.5
184+
}));
185+
186+
assertVisibility(opts, ['none', 'none', 'visible', 'none']);
187+
expect(opts.circle.paint['circle-color']).toBe('red', 'circle-color');
188+
expect(opts.circle.paint['circle-radius']).toBe(5, 'circle-radius');
189+
190+
expect(opts.circle.paint['circle-opacity']).toEqual({
191+
property: 'circle-opacity',
192+
stops: [ [0, 0.5], [1, 0], [2, 0.25], [6, 0.4] ]
193+
}, 'circle-opacity stops');
194+
195+
var circleProps = opts.circle.geojson.features.map(function(f) {
196+
return f.properties;
197+
});
198+
199+
200+
expect(circleProps).toEqual([
201+
{ 'circle-opacity': 0 },
202+
{ 'circle-opacity': 1 },
203+
{ 'circle-opacity': 2 },
204+
// lat === null
205+
// lon === null
206+
{ 'circle-opacity': 1 },
207+
{ 'circle-opacity': 6 },
208+
], 'geojson feature properties');
209+
});
210+
172211
it('should generate correct output for fill + markers + lines traces', function() {
173212
var opts = _convert(Lib.extendFlat({}, base, {
174213
mode: 'markers+lines',
@@ -510,7 +549,6 @@ describe('@noCI scattermapbox hover', function() {
510549
});
511550
});
512551

513-
514552
describe('@noCI Test plotly events on a scattermapbox plot:', function() {
515553
var mock = require('@mocks/mapbox_0.json');
516554

0 commit comments

Comments
 (0)