Skip to content

Miscellaneous mapbox tweaks #681

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 23, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions src/plots/mapbox/convert_text_opts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2012-2016, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/


'use strict';

var Lib = require('../../lib');


/**
* Convert plotly.js 'textposition' to mapbox-gl 'anchor' and 'offset'
* (with the help of the icon size).
*
* @param {string} textpostion : plotly.js textposition value
* @param {number} iconSize : plotly.js icon size (e.g. marker.size for traces)
*
* @return {object}
* - anchor
* - offset
*/
module.exports = function convertTextOpts(textposition, iconSize) {
var parts = textposition.split(' '),
vPos = parts[0],
hPos = parts[1];

// ballpack values
var factor = Array.isArray(iconSize) ? Lib.mean(iconSize) : iconSize,
xInc = 0.5 + (factor / 100),
yInc = 1.5 + (factor / 100);

var anchorVals = ['', ''],
offset = [0, 0];

switch(vPos) {
case 'top':
anchorVals[0] = 'top';
offset[1] = -yInc;
break;
case 'bottom':
anchorVals[0] = 'bottom';
offset[1] = yInc;
break;
}

switch(hPos) {
case 'left':
anchorVals[1] = 'right';
offset[0] = -xInc;
break;
case 'right':
anchorVals[1] = 'left';
offset[0] = xInc;
break;
}

// Mapbox text-anchor must be one of:
// center, left, right, top, bottom,
// top-left, top-right, bottom-left, bottom-right

var anchor;
if(anchorVals[0] && anchorVals[1]) anchor = anchorVals.join('-');
else if(anchorVals[0]) anchor = anchorVals[0];
else if(anchorVals[1]) anchor = anchorVals[1];
else anchor = 'center';

return { anchor: anchor, offset: offset };
};
65 changes: 53 additions & 12 deletions src/plots/mapbox/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
'use strict';

var Lib = require('../../lib');
var convertTextOpts = require('./convert_text_opts');


function MapboxLayer(mapbox, index) {
Expand Down Expand Up @@ -45,9 +46,15 @@ proto.update = function update(opts) {
};

proto.needsNewSource = function(opts) {

// for some reason changing layer to 'fill' or 'symbol'
// w/o changing the source throws an exception in mapbox-gl 0.18 ;
// stay safe and make new source on type changes

return (
this.sourceType !== opts.sourcetype ||
this.source !== opts.source
this.source !== opts.source ||
this.layerType !== opts.type
);
};

Expand Down Expand Up @@ -95,10 +102,11 @@ proto.updateLayer = function(opts) {
};

proto.updateStyle = function(opts) {
var paintOpts = convertPaintOpts(opts);
var convertedOpts = convertOpts(opts);

if(isVisible(opts)) {
this.mapbox.setOptions(this.idLayer, 'setPaintProperty', paintOpts);
this.mapbox.setOptions(this.idLayer, 'setLayoutProperty', convertedOpts.layout);
this.mapbox.setOptions(this.idLayer, 'setPaintProperty', convertedOpts.paint);
}
};

Expand All @@ -121,31 +129,64 @@ function isVisible(opts) {
);
}

function convertPaintOpts(opts) {
var paintOpts = {};
function convertOpts(opts) {
var layout = {},
paint = {};

switch(opts.type) {

case 'circle':
Lib.extendFlat(paint, {
'circle-radius': opts.circle.radius,
'circle-color': opts.color,
'circle-opacity': opts.opacity
});
break;

case 'line':
Lib.extendFlat(paintOpts, {
Lib.extendFlat(paint, {
'line-width': opts.line.width,
'line-color': opts.line.color,
'line-color': opts.color,
'line-opacity': opts.opacity
});
break;

case 'fill':
Lib.extendFlat(paintOpts, {
'fill-color': opts.fillcolor,
'fill-outline-color': opts.line.color,
Lib.extendFlat(paint, {
'fill-color': opts.color,
'fill-outline-color': opts.fill.outlinecolor,
'fill-opacity': opts.opacity

// no way to pass line.width at the moment
// no way to pass specify outline width at the moment
});
break;

case 'symbol':
var symbol = opts.symbol,
textOpts = convertTextOpts(symbol.textposition, symbol.iconsize);

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

'text-field': symbol.text,
'text-size': symbol.textfont.size,
'text-anchor': textOpts.anchor,
'text-offset': textOpts.offset

// TODO font family
//'text-font': symbol.textfont.family.split(', '),
});

Lib.extendFlat(paint, {
'icon-color': opts.color,
'text-color': symbol.textfont.color,
'text-opacity': opts.opacity
});
break;
}

return paintOpts;
return { layout: layout, paint: paint };
}

function convertSourceOpts(opts) {
Expand Down
112 changes: 96 additions & 16 deletions src/plots/mapbox/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

'use strict';

var scatterMapboxAttrs = require('../../traces/scattermapbox/attributes');
var Lib = require('../../lib');
var defaultLine = require('../../components/color').defaultLine;
var extendFlat = require('../../lib').extendFlat;

var lineAttrs = scatterMapboxAttrs.line;
var fontAttrs = require('../font_attributes');
var textposition = require('../../traces/scatter/attributes').textposition;


module.exports = {
Expand Down Expand Up @@ -129,15 +128,18 @@ module.exports = {

type: {
valType: 'enumerated',
values: ['line', 'fill'],
dflt: 'line',
values: ['circle', 'line', 'fill', 'symbol'],
dflt: 'circle',
role: 'info',
description: [
'Sets the layer type.',
'Support for *raster*, *background* types is coming soon.'
'Support for *raster*, *background* types is coming soon.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

presumably , *symbol* too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. I could add it now if you want.

'Note that *line* and *fill* are not compatible with Point',
'GeoJSON geometries.'
].join(' ')
},

// attributes shared between all types
below: {
valType: 'string',
dflt: '',
Expand All @@ -149,23 +151,101 @@ module.exports = {
'the layer will be inserted above every existing layer.'
].join(' ')
},

line: {
color: extendFlat({}, lineAttrs.color, {
dflt: defaultLine
}),
width: lineAttrs.width
color: {
valType: 'color',
dflt: defaultLine,
role: 'style',
description: [
'Sets the primary layer color.',
'If `type` is *circle*, color corresponds to the circle color',
'If `type` is *line*, color corresponds to the line color',
'If `type` is *fill*, color corresponds to the fill color',
'If `type` is *symbol*, color corresponds to the icon color'
].join(' ')
Copy link
Contributor Author

@etpinard etpinard Jun 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleaner, right?

},

fillcolor: scatterMapboxAttrs.fillcolor,

opacity: {
valType: 'number',
min: 0,
max: 1,
dflt: 1,
role: 'info',
description: 'Sets the opacity of the layer.'
},

// type-specific style attributes
circle: {
radius: {
valType: 'number',
dflt: 15,
role: 'style',
description: [
'Sets the circle radius.',
'Has an effect only when `type` is set to *circle*.'
].join(' ')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing opacity here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

},

line: {
width: {
valType: 'number',
dflt: 2,
role: 'style',
description: [
'Sets the line radius.',
'Has an effect only when `type` is set to *line*.'
].join(' ')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing opacity here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

layers of different type don't need different opacity value; opacity is layer-wide.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah, isee now

},

fill: {
outlinecolor: {
valType: 'color',
dflt: defaultLine,
role: 'style',
description: [
'Sets the fill outline color.',
'Has an effect only when `type` is set to *fill*.'
].join(' ')
}
},

symbol: {
icon: {
valType: 'string',
dflt: 'marker',
role: 'style',
description: [
'Sets the symbol icon image.',
'Full list: https://www.mapbox.com/maki-icons/'
].join(' ')
},
iconsize: {
valType: 'number',
dflt: 10,
role: 'style',
description: [
'Sets the symbol icon size.',
'Has an effect only when `type` is set to *symbol*.'
].join(' ')
},
text: {
valType: 'string',
dflt: '',
role: 'info',
description: [
'Sets the symbol text.'
].join(' ')
},
textfont: Lib.extendDeep({}, fontAttrs, {
description: [
'Sets the icon text font.',
'Has an effect only when `type` is set to *symbol*.'
].join(' '),
family: {
dflt: 'Open Sans Regular, Arial Unicode MS Regular'
}
}),
textposition: Lib.extendFlat({}, textposition, { arrayOk: false })
}
}

Expand Down
32 changes: 22 additions & 10 deletions src/plots/mapbox/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,29 +49,41 @@ function handleLayerDefaults(containerIn, containerOut) {
}

for(var i = 0; i < layersIn.length; i++) {
layerIn = layersIn[i];
layerIn = layersIn[i] || {};
layerOut = {};

var sourceType = coerce('sourcetype');
coerce('source');

if(sourceType === 'vector') coerce('sourcelayer');

// maybe add smart default based off 'fillcolor' ???
// maybe add smart default based off GeoJSON geometry?
var type = coerce('type');

var lineColor;
if(type === 'line' || type === 'fill') {
lineColor = coerce('line.color');
coerce('below');
coerce('color');
coerce('opacity');

if(type === 'circle') {
coerce('circle.radius');
}

// no way to pass line.width to fill layers
if(type === 'line') coerce('line.width');
if(type === 'line') {
coerce('line.width');
}

if(type === 'fill') coerce('fillcolor', lineColor);
if(type === 'fill') {
coerce('fill.outlinecolor');
}

coerce('below');
coerce('opacity');
if(type === 'symbol') {
coerce('symbol.icon');
coerce('symbol.iconsize');

coerce('symbol.text');
Lib.coerceFont(coerce, 'symbol.textfont');
coerce('symbol.textposition');
}

layersOut.push(layerOut);
}
Expand Down
Loading