Skip to content

Commit

Permalink
Merge pull request #4069 from plotly/mapbox-attributions-inject-css
Browse files Browse the repository at this point in the history
mapbox: add attributions
  • Loading branch information
antoinerg authored Jul 22, 2019
2 parents 56dd829 + 4db3809 commit 1bebca6
Show file tree
Hide file tree
Showing 31 changed files with 470 additions and 25 deletions.
186 changes: 167 additions & 19 deletions src/plots/mapbox/constants.js

Large diffs are not rendered by default.

89 changes: 88 additions & 1 deletion src/plots/mapbox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ var mapboxgl = require('mapbox-gl');
var Lib = require('../../lib');
var getSubplotCalcData = require('../../plots/get_data').getSubplotCalcData;
var xmlnsNamespaces = require('../../constants/xmlns_namespaces');
var d3 = require('d3');
var Drawing = require('../../components/drawing');
var svgTextUtils = require('../../lib/svg_text_utils');

var Mapbox = require('./mapbox');

Expand Down Expand Up @@ -118,7 +121,91 @@ exports.toSVG = function(gd) {
preserveAspectRatio: 'none'
});

mapbox.destroy();
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', 'translate(' + (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('<br>');
attributionText
.text(multilineAttributions)
.attr('data-unformatted', multilineAttributions)
.call(svgTextUtils.convertToTspans, gd);

bBox = Drawing.bBox(attributionText.node());
}
attributionText.attr('transform', 'translate(-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', 'translate(' + offset[0] + ',' + offset[1] + ') scale(' + scaleRatio + ')');
}
};

Expand Down
2 changes: 2 additions & 0 deletions src/plots/mapbox/layers.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ function convertSourceOpts(opts) {

sourceOpts[field] = source;

if(opts.sourceattribution) sourceOpts.attribution = opts.sourceattribution;

return sourceOpts;
}

Expand Down
8 changes: 8 additions & 0 deletions src/plots/mapbox/layout_attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ var attrs = module.exports = overrideAll({
].join(' ')
},

sourceattribution: {
valType: 'string',
role: 'info',
description: [
'Sets the attribution for this source.'
].join(' ')
},

type: {
valType: 'enumerated',
values: ['circle', 'line', 'fill', 'symbol', 'raster'],
Expand Down
1 change: 1 addition & 0 deletions src/plots/mapbox/layout_defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function handleLayerDefaults(layerIn, layerOut) {
var mustBeRasterLayer = sourceType === 'raster' || sourceType === 'image';

coerce('source');
coerce('sourceattribution');

if(sourceType === 'vector') {
coerce('sourcelayer');
Expand Down
14 changes: 10 additions & 4 deletions src/plots/mapbox/mapbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,14 @@ proto.createMap = function(calcData, fullLayout, resolve, reject) {
preserveDrawingBuffer: self.isStatic,

doubleClickZoom: false,
boxZoom: false
});
boxZoom: false,

attributionControl: false
})
.addControl(new mapboxgl.AttributionControl({
compact: true
}));


// make sure canvas does not inherit left and top css
map._canvas.style.left = '0px';
Expand Down Expand Up @@ -763,8 +769,8 @@ function getStyleObj(val) {

if(constants.styleValuesMapbox.indexOf(val) !== -1) {
styleObj.style = convertStyleVal(val);
} else if(val === constants.styleValueOSM) {
styleObj.style = constants.styleOSM;
} else if(constants.styles[val]) {
styleObj.style = constants.styles[val];
} else {
styleObj.style = val;
}
Expand Down
2 changes: 1 addition & 1 deletion src/registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ function registerTraceModule(_module) {
if(bpmName === 'mapbox') {
var styleRules = basePlotModule.constants.styleRules;
for(var k in styleRules) {
addStyleRule('.mapboxgl-' + k, styleRules[k]);
addStyleRule('.js-plotly-plot .plotly .mapboxgl-' + k, styleRules[k]);
}
}

Expand Down
Binary file modified test/image/baselines/mapbox_0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_angles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_bubbles-text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_bubbles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/mapbox_carto-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_choropleth-multiple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_choropleth-raw-geojson.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_choropleth0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_connectgaps.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_custom-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_density-mulitple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_density0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_earthquake-density.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_fill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_geojson-attributes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_layers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_osm-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/mapbox_stamen-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/image/baselines/mapbox_symbol-text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/image/baselines/mapbox_white-bg-style.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions test/image/mocks/mapbox_carto-style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"data": [
{
"type": "scattermapbox",
"name": "carto-positron",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ]
},
{
"type": "scattermapbox",
"name": "carto-darkmatter",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ],
"subplot": "mapbox2"
}
],
"layout": {
"grid": {"rows": 1, "columns": 2},

"legend": {
"x": 0,
"y": 1, "yanchor": "bottom"
},

"mapbox": {
"domain": {"row": 0, "column": 0},
"style": "carto-positron"
},
"mapbox2": {
"domain": {"row": 0, "column": 1},
"style": "carto-darkmatter"
}
}
}
44 changes: 44 additions & 0 deletions test/image/mocks/mapbox_stamen-style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"data": [
{
"type": "scattermapbox",
"name": "stamen-terrain",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ]
},
{
"type": "scattermapbox",
"name": "stamen-toner",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ],
"subplot": "mapbox2"
},
{
"type": "scattermapbox",
"name": "stamen-watercolor",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ],
"subplot": "mapbox3"
}
],
"layout": {
"grid": {"rows": 1, "columns": 3},

"legend": {
"x": 0,
"y": 1, "yanchor": "bottom"
},
"mapbox": {
"domain": {"row": 0, "column": 0},
"style": "stamen-terrain"
},
"mapbox2": {
"domain": {"row": 0, "column": 1},
"style": "stamen-toner"
},
"mapbox3": {
"domain": {"row": 0, "column": 2},
"style": "stamen-watercolor"
}
}
}
18 changes: 18 additions & 0 deletions test/image/mocks/mapbox_white-bg-style.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"data": [
{
"type": "scattermapbox",
"name": "white-bg",
"lon": [ 10, 20 ],
"lat": [ 20, 10 ]
}
],
"layout": {
"width": 200,
"height": 200,
"margin": {"t": 0, "b": 0, "l": 0, "r": 0},
"mapbox": {
"style": "white-bg"
}
}
}
97 changes: 97 additions & 0 deletions test/jasmine/tests/mapbox_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1201,6 +1201,103 @@ describe('@noCI, mapbox plots', function() {
.then(done);
}, LONG_TIMEOUT_INTERVAL);

describe('attributions', function() {
it('@gl should be displayed for style "open-street-map"', function(done) {
Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'open-street-map'}})
.then(function() {
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
expect(s.size()).toBe(1);
expect(s.text()).toEqual('© OpenStreetMap');
})
.catch(failTest)
.then(done);
});

it('@gl should be displayed for style from Mapbox', function(done) {
Plotly.newPlot(gd, [{type: 'scattermapbox'}], {mapbox: {style: 'basic'}})
.then(function() {
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
expect(s.size()).toBe(1);
expect(s.text()).toEqual('© Mapbox © OpenStreetMap Improve this map');
})
.catch(failTest)
.then(done);
});

function mockLayoutCustomStyle() {
return {
'mapbox': {
'style': {
'id': 'osm',
'version': 8,
'sources': {
'simple-tiles': {
'type': 'raster',
'tiles': [
'https://a.tile.openstreetmap.org/{z}/{x}/{y}.png',
'https://b.tile.openstreetmap.org/{z}/{x}/{y}.png'
],
'tileSize': 256
}
},
'layers': [
{
'id': 'simple-tiles',
'type': 'raster',
'source': 'simple-tiles',
'minzoom': 0,
'maxzoom': 22
}
]
}
}
};
}

it('@gl should not be displayed for custom style without attribution', function(done) {
Plotly.newPlot(gd, [{type: 'scattermapbox'}], mockLayoutCustomStyle())
.then(function() {
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
expect(s.size()).toBe(1);
expect(s.text()).toEqual('');
})
.catch(failTest)
.then(done);
});

it('@gl should be displayed for custom style with attribution', function(done) {
var attr = 'custom attribution';
var layout = mockLayoutCustomStyle();
layout.mapbox.style.sources['simple-tiles'].attribution = attr;
Plotly.newPlot(gd, [{type: 'scattermapbox'}], layout)
.then(function() {
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
expect(s.size()).toBe(1);
expect(s.text()).toEqual(attr);
})
.catch(failTest)
.then(done);
});

it('@gl should be displayed for attributions defined in layers\' sourceattribution', function(done) {
var mock = require('@mocks/mapbox_layers.json');
var customMock = Lib.extendDeep(mock);

var attr = 'super custom attribution';
customMock.data.pop();
customMock.layout.mapbox.layers[0].sourceattribution = attr;

Plotly.newPlot(gd, customMock)
.then(function() {
var s = Plotly.d3.selectAll('.mapboxgl-ctrl-attrib');
expect(s.size()).toBe(1);
expect(s.text()).toEqual([attr, '© Mapbox © OpenStreetMap Improve this map'].join(' | '));
})
.catch(failTest)
.then(done);
});
});

function getMapInfo(gd) {
var subplot = gd._fullLayout.mapbox._subplot;
var map = subplot.map;
Expand Down

0 comments on commit 1bebca6

Please sign in to comment.