package.src.plots.mapbox.index.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plotly.js Show documentation
Show all versions of plotly.js Show documentation
The open source javascript graphing library that powers plotly
The newest version!
'use strict';
var mapboxgl = require('@plotly/mapbox-gl/dist/mapbox-gl-unminified');
var Lib = require('../../lib');
var strTranslate = Lib.strTranslate;
var strScale = Lib.strScale;
var getSubplotCalcData = require('../../plots/get_data').getSubplotCalcData;
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
var d3 = require('@plotly/d3');
var Drawing = require('../../components/drawing');
var svgTextUtils = require('../../lib/svg_text_utils');
var Mapbox = require('./mapbox');
var MAPBOX = 'mapbox';
var constants = exports.constants = require('./constants');
exports.name = MAPBOX;
exports.attr = 'subplot';
exports.idRoot = MAPBOX;
exports.idRegex = exports.attrRegex = Lib.counterRegex(MAPBOX);
exports.attributes = {
subplot: {
valType: 'subplotid',
dflt: 'mapbox',
editType: 'calc',
description: [
'Sets a reference between this trace\'s data coordinates and',
'a mapbox subplot.',
'If *mapbox* (the default value), the data refer to `layout.mapbox`.',
'If *mapbox2*, the data refer to `layout.mapbox2`, and so on.'
].join(' ')
}
};
exports.layoutAttributes = require('./layout_attributes');
exports.supplyLayoutDefaults = require('./layout_defaults');
exports.plot = function plot(gd) {
var fullLayout = gd._fullLayout;
var calcData = gd.calcdata;
var mapboxIds = fullLayout._subplots[MAPBOX];
if(mapboxgl.version !== constants.requiredVersion) {
throw new Error(constants.wrongVersionErrorMsg);
}
var accessToken = findAccessToken(gd, mapboxIds);
mapboxgl.accessToken = accessToken;
for(var i = 0; i < mapboxIds.length; i++) {
var id = mapboxIds[i];
var subplotCalcData = getSubplotCalcData(calcData, MAPBOX, id);
var opts = fullLayout[id];
var mapbox = opts._subplot;
if(!mapbox) {
mapbox = new Mapbox(gd, id);
fullLayout[id]._subplot = mapbox;
}
if(!mapbox.viewInitial) {
mapbox.viewInitial = {
center: Lib.extendFlat({}, opts.center),
zoom: opts.zoom,
bearing: opts.bearing,
pitch: opts.pitch
};
}
mapbox.plot(subplotCalcData, fullLayout, gd._promises);
}
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
var oldMapboxKeys = oldFullLayout._subplots[MAPBOX] || [];
for(var i = 0; i < oldMapboxKeys.length; i++) {
var oldMapboxKey = oldMapboxKeys[i];
if(!newFullLayout[oldMapboxKey] && !!oldFullLayout[oldMapboxKey]._subplot) {
oldFullLayout[oldMapboxKey]._subplot.destroy();
}
}
};
exports.toSVG = function(gd) {
var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots[MAPBOX];
var size = fullLayout._size;
for(var i = 0; i < subplotIds.length; i++) {
var opts = fullLayout[subplotIds[i]];
var domain = opts.domain;
var mapbox = opts._subplot;
var imageData = mapbox.toImage('png');
var image = fullLayout._glimages.append('svg:image');
image.attr({
xmlns: xmlnsNamespaces.svg,
'xlink:href': imageData,
x: size.l + size.w * domain.x[0],
y: size.t + size.h * (1 - domain.y[1]),
width: size.w * (domain.x[1] - domain.x[0]),
height: size.h * (domain.y[1] - domain.y[0]),
preserveAspectRatio: 'none'
});
var subplotDiv = d3.select(opts._subplot.div);
// Append logo if visible
var hidden = subplotDiv.select('.mapboxgl-ctrl-logo').node().offsetParent === null;
if(!hidden) {
var logo = fullLayout._glimages.append('g');
logo.attr('transform', strTranslate(size.l + size.w * domain.x[0] + 10, size.t + size.h * (1 - domain.y[0]) - 31));
logo.append('path')
.attr('d', constants.mapboxLogo.path0)
.style({
opacity: 0.9,
fill: '#ffffff',
'enable-background': 'new'
});
logo.append('path')
.attr('d', constants.mapboxLogo.path1)
.style('opacity', 0.35)
.style('enable-background', 'new');
logo.append('path')
.attr('d', constants.mapboxLogo.path2)
.style('opacity', 0.35)
.style('enable-background', 'new');
logo.append('polygon')
.attr('points', constants.mapboxLogo.polygon)
.style({
opacity: 0.9,
fill: '#ffffff',
'enable-background': 'new'
});
}
// Add attributions
var attributions = subplotDiv
.select('.mapboxgl-ctrl-attrib').text()
.replace('Improve this map', '');
var attributionGroup = fullLayout._glimages.append('g');
var attributionText = attributionGroup.append('text');
attributionText
.text(attributions)
.classed('static-attribution', true)
.attr({
'font-size': 12,
'font-family': 'Arial',
color: 'rgba(0, 0, 0, 0.75)',
'text-anchor': 'end',
'data-unformatted': attributions
});
var bBox = Drawing.bBox(attributionText.node());
// Break into multiple lines twice larger than domain
var maxWidth = size.w * (domain.x[1] - domain.x[0]);
if((bBox.width > maxWidth / 2)) {
var multilineAttributions = attributions.split('|').join('
');
attributionText
.text(multilineAttributions)
.attr('data-unformatted', multilineAttributions)
.call(svgTextUtils.convertToTspans, gd);
bBox = Drawing.bBox(attributionText.node());
}
attributionText.attr('transform', strTranslate(-3, -bBox.height + 8));
// Draw white rectangle behind text
attributionGroup
.insert('rect', '.static-attribution')
.attr({
x: -bBox.width - 6,
y: -bBox.height - 3,
width: bBox.width + 6,
height: bBox.height + 3,
fill: 'rgba(255, 255, 255, 0.75)'
});
// Scale down if larger than domain
var scaleRatio = 1;
if((bBox.width + 6) > maxWidth) scaleRatio = maxWidth / (bBox.width + 6);
var offset = [(size.l + size.w * domain.x[1]), (size.t + size.h * (1 - domain.y[0]))];
attributionGroup.attr('transform', strTranslate(offset[0], offset[1]) + strScale(scaleRatio));
}
};
// N.B. mapbox-gl only allows one accessToken to be set per page:
// https://github.com/mapbox/mapbox-gl-js/issues/6331
function findAccessToken(gd, mapboxIds) {
var fullLayout = gd._fullLayout;
var context = gd._context;
// special case for Mapbox Atlas users
if(context.mapboxAccessToken === '') return '';
var tokensUseful = [];
var tokensListed = [];
var hasOneSetMapboxStyle = false;
var wontWork = false;
// Take the first token we find in a mapbox subplot.
// These default to the context value but may be overridden.
for(var i = 0; i < mapboxIds.length; i++) {
var opts = fullLayout[mapboxIds[i]];
var token = opts.accesstoken;
if(isStyleRequireAccessToken(opts.style)) {
if(token) {
Lib.pushUnique(tokensUseful, token);
} else {
if(isStyleRequireAccessToken(opts._input.style)) {
Lib.error('Uses Mapbox map style, but did not set an access token.');
hasOneSetMapboxStyle = true;
}
wontWork = true;
}
}
if(token) {
Lib.pushUnique(tokensListed, token);
}
}
if(wontWork) {
var msg = hasOneSetMapboxStyle ?
constants.noAccessTokenErrorMsg :
constants.missingStyleErrorMsg;
Lib.error(msg);
throw new Error(msg);
}
if(tokensUseful.length) {
if(tokensUseful.length > 1) {
Lib.warn(constants.multipleTokensErrorMsg);
}
return tokensUseful[0];
} else {
if(tokensListed.length) {
Lib.log([
'Listed mapbox access token(s)', tokensListed.join(','),
'but did not use a Mapbox map style, ignoring token(s).'
].join(' '));
}
return '';
}
}
function isStyleRequireAccessToken(s) {
return typeof s === 'string' && (
constants.styleValuesMapbox.indexOf(s) !== -1 ||
s.indexOf('mapbox://') === 0 ||
s.indexOf('stamen') === 0
);
}
exports.updateFx = function(gd) {
var fullLayout = gd._fullLayout;
var subplotIds = fullLayout._subplots[MAPBOX];
for(var i = 0; i < subplotIds.length; i++) {
var subplotObj = fullLayout[subplotIds[i]]._subplot;
subplotObj.updateFx(fullLayout);
}
};