blob: 8db8950404e30e3c55e2642e52922f44194538c8 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/* eslint-disable react/sort-prop-types */
import d3 from 'd3';
import PropTypes from 'prop-types';
import { extent as d3Extent } from 'd3-array';
import { getNumberFormatter, getSequentialSchemeRegistry } from '@superset-ui/core';
import Datamap from 'datamaps/dist/datamaps.world.min';
const propTypes = {
data: PropTypes.arrayOf(
PropTypes.shape({
country: PropTypes.string,
latitude: PropTypes.number,
longitude: PropTypes.number,
name: PropTypes.string,
m1: PropTypes.number,
m2: PropTypes.number,
}),
),
height: PropTypes.number,
maxBubbleSize: PropTypes.number,
showBubbles: PropTypes.bool,
linearColorScheme: PropTypes.string,
color: PropTypes.string,
};
const formatter = getNumberFormatter();
function WorldMap(element, props) {
const { data, width, height, maxBubbleSize, showBubbles, linearColorScheme, color } = props;
const div = d3.select(element);
div.classed('superset-legacy-chart-world-map', true);
div.selectAll('*').remove();
// Ignore XXX's to get better normalization
const filteredData = data.filter(d => d.country && d.country !== 'XXX');
const extRadius = d3.extent(filteredData, d => Math.sqrt(d.m2));
const radiusScale = d3.scale
.linear()
.domain([extRadius[0], extRadius[1]])
.range([1, maxBubbleSize]);
const colorScale = getSequentialSchemeRegistry()
.get(linearColorScheme)
.createLinearScale(d3Extent(filteredData, d => d.m1));
const processedData = filteredData.map(d => ({
...d,
radius: radiusScale(Math.sqrt(d.m2)),
fillColor: colorScale(d.m1),
}));
const mapData = {};
processedData.forEach(d => {
mapData[d.country] = d;
});
const map = new Datamap({
element,
width,
height,
data: processedData,
fills: {
defaultFill: '#eee',
},
geographyConfig: {
popupOnHover: true,
highlightOnHover: true,
borderWidth: 1,
borderColor: '#feffff',
highlightBorderColor: '#feffff',
highlightFillColor: color,
highlightBorderWidth: 1,
popupTemplate: (geo, d) =>
`<div class="hoverinfo"><strong>${d.name}</strong><br>${formatter(d.m1)}</div>`,
},
bubblesConfig: {
borderWidth: 1,
borderOpacity: 1,
borderColor: color,
popupOnHover: true,
radius: null,
popupTemplate: (geo, d) =>
`<div class="hoverinfo"><strong>${d.name}</strong><br>${formatter(d.m2)}</div>`,
fillOpacity: 0.5,
animate: true,
highlightOnHover: true,
highlightFillColor: color,
highlightBorderColor: 'black',
highlightBorderWidth: 2,
highlightBorderOpacity: 1,
highlightFillOpacity: 0.85,
exitDelay: 100,
key: JSON.stringify,
},
});
map.updateChoropleth(mapData);
if (showBubbles) {
map.bubbles(processedData);
div.selectAll('circle.datamaps-bubble').style('fill', color).style('stroke', color);
}
}
WorldMap.displayName = 'WorldMap';
WorldMap.propTypes = propTypes;
export default WorldMap;