hansight-datamap
Version:
Data of world map by hansight-llp
1,913 lines (1,798 loc) • 637 kB
JavaScript
(function() {
var svg;
// Save off default references
var d3 = window.d3,
topojson = window.topojson;
var defaultOptions = {
scope: 'world',
responsive: false,
aspectRatio: 0.5625,
setProjection: setProjection,
projection: 'equirectangular',
dataType: 'json',
data: {},
done: function() {},
fills: {
defaultFill: '#ABDDA4'
},
filters: {},
geographyConfig: {
dataUrl: null,
hideAntarctica: true,
hideHawaiiAndAlaska: false,
borderWidth: 1,
borderOpacity: 1,
borderColor: '#FDFDFD',
popupTemplate: function(geography, data) {
return '<div class="hoverinfo"><strong>' + geography.properties.name + '</strong></div>';
},
popupOnHover: true,
highlightOnHover: true,
highlightFillColor: '#FC8D59',
highlightBorderColor: 'rgba(250, 15, 160, 0.2)',
highlightBorderWidth: 2,
highlightBorderOpacity: 1
},
projectionConfig: {
rotation: [97, 0]
},
bubblesConfig: {
borderWidth: 0,
borderOpacity: 0,
borderColor: '#FFFFFF',
popupOnHover: true,
radius: null,
popupTemplate: function(geography, data) {
return '<div class="hoverinfo"><strong>' + data.name + '</strong></div>';
},
fillOpacity: 0.75,
animate: true,
highlightOnHover: true,
highlightFillColor: '#FC8D59',
highlightBorderColor: 'rgba(250, 15, 160, 0.2)',
highlightBorderWidth: 2,
highlightBorderOpacity: 1,
highlightFillOpacity: 0.85,
exitDelay: 100,
key: JSON.stringify
},
arcConfig: {
strokeColor: '#DD1C77',
strokeWidth: 1,
arcSharpness: 1,
animationSpeed: 600,
popupOnHover: false,
popupTemplate: function(geography, data) {
// Case with latitude and longitude
if ((data.origin && data.destination) && data.origin.latitude && data.origin.longitude && data.destination.latitude && data.destination.longitude) {
return '<div class="hoverinfo"><strong>Arc</strong><br>Origin: ' + JSON.stringify(data.origin) + '<br>Destination: ' + JSON.stringify(data.destination) + '</div>';
}
// Case with only country name
else if (data.origin && data.destination) {
return '<div class="hoverinfo"><strong>Arc</strong><br>' + data.origin + ' -> ' + data.destination + '</div>';
}
// Missing information
else {
return '';
}
}
}
};
/*
Getter for value. If not declared on datumValue, look up the chain into optionsValue
*/
function val(datumValue, optionsValue, context) {
if (typeof context === 'undefined') {
context = optionsValue;
optionsValues = undefined;
}
var value = typeof datumValue !== 'undefined' ? datumValue : optionsValue;
if (typeof value === 'undefined') {
return null;
}
if (typeof value === 'function') {
var fnContext = [context];
if (context.geography) {
fnContext = [context.geography, context.data];
}
return value.apply(null, fnContext);
} else {
return value;
}
}
function addContainer(element, height, width) {
this.svg = d3.select(element).append('svg')
.attr('width', width || element.offsetWidth)
.attr('data-width', width || element.offsetWidth)
.attr('class', 'datamap')
.attr('height', height || element.offsetHeight)
.style('overflow', 'hidden'); // IE10+ doesn't respect height/width when map is zoomed in
if (this.options.responsive) {
d3.select(this.options.element).style({
'position': 'relative',
'padding-bottom': (this.options.aspectRatio * 100) + '%'
});
d3.select(this.options.element).select('svg').style({
'position': 'absolute',
'width': '100%',
'height': '100%'
});
d3.select(this.options.element).select('svg').select('g').selectAll('path').style('vector-effect', 'non-scaling-stroke');
}
var defs = this.svg.append("svg:defs")
// left -> right bright -> dark
var gradient = defs.append("svg:linearGradient")
.attr("id", "gradientLR")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%")
.attr("spreadMethod", "pad");
gradient.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 1);
gradient.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 0);
// right -> left bright -> dark
gradient = defs.append("svg:linearGradient")
.attr("id", "gradientRL")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%")
.attr("spreadMethod", "pad");
gradient.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 0);
gradient.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 1);
// top -> bottom bright -> dark
gradient = defs.append("svg:linearGradient")
.attr("id", "gradientTB")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "0%")
.attr("y2", "100%")
.attr("spreadMethod", "pad");
gradient.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 1);
gradient.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 0);
// bottom -> top bright -> dark
gradient = defs.append("svg:linearGradient")
.attr("id", "gradientBT")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "0%")
.attr("y2", "100%")
.attr("spreadMethod", "pad");
gradient.append("svg:stop")
.attr("offset", "0%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 0);
gradient.append("svg:stop")
.attr("offset", "100%")
.attr("stop-color", "#FE9900")
.attr("stop-opacity", 1);
return this.svg;
}
// setProjection takes the svg element and options
function setProjection(element, options) {
var width = options.width || element.offsetWidth;
var height = options.height || element.offsetHeight;
var projection, path;
var svg = this.svg;
if (options && typeof options.scope === 'undefined') {
options.scope = 'world';
}
if (options.scope === 'usa') {
projection = d3.geo.albersUsa()
.scale(width)
.translate([width / 2, height / 2]);
} else if (options.scope === 'world') {
projection = d3.geo[options.projection]()
.scale((width + 1) / 2 / Math.PI)
.translate([width / 2, height / (options.projection === "mercator" ? 1.45 : 1.8)]);
// .rotate([-115, 0]);
}
if (options.projection === 'orthographic') {
svg.append("defs").append("path")
.datum({
type: "Sphere"
})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
projection.scale(250).clipAngle(90).rotate(options.projectionConfig.rotation)
}
path = d3.geo.path()
.projection(projection);
return {
path: path,
projection: projection
};
}
function addStyleBlock() {
if (d3.select('.datamaps-style-block').empty()) {
d3.select('head').append('style').attr('class', 'datamaps-style-block')
.html('.datamap path.datamaps-graticule { fill: none; stroke: #777; stroke-width: 0.5px; stroke-opacity: .5; pointer-events: none; } .datamap .labels {pointer-events: none;} .datamap path:not(.datamaps-arc), .datamap circle, .datamap line {stroke: #FFFFFF; vector-effect: non-scaling-stroke; stroke-width: 1px;} .datamaps-legend dt, .datamaps-legend dd { float: left; margin: 0 3px 0 0;} .datamaps-legend dd {width: 20px; margin-right: 6px; border-radius: 3px;} .datamaps-legend {padding-bottom: 20px; z-index: 1001; position: absolute; left: 4px; font-size: 12px; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;} .datamaps-hoverover {display: none; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } .hoverinfo {padding: 4px; border-radius: 1px; background-color: #FFF; box-shadow: 1px 1px 5px #CCC; font-size: 12px; border: 1px solid #CCC; } .hoverinfo hr {border:1px dotted #CCC; }');
}
}
function drawSubunits(data) {
var fillData = this.options.fills,
colorCodeData = this.options.data || {},
geoConfig = this.options.geographyConfig;
var subunits = this.svg.select('g.datamaps-subunits');
if (subunits.empty()) {
subunits = this.addLayer('datamaps-subunits', null, true);
}
var geoData = topojson.feature(data, data.objects[this.options.scope]).features;
if (geoConfig.hideAntarctica) {
geoData = geoData.filter(function(feature) {
return feature.id !== "ATA";
});
}
if (geoConfig.hideHawaiiAndAlaska) {
geoData = geoData.filter(function(feature) {
return feature.id !== "HI" && feature.id !== 'AK';
});
}
// $topojson --allow-empty -o final2.json -- chn.json world.json
var chnData = topojson.feature(data, data.objects['chn']).features;
geoData = chnData.concat(geoData);
var geo = subunits.selectAll('path.datamaps-subunit').data(geoData);
geo.enter()
.append('path')
.attr('d', this.path)
.attr('class', function(d) {
return 'datamaps-subunit ' + d.id;
})
.attr('data-info', function(d) {
return JSON.stringify(colorCodeData[d.id]);
})
.style('fill', function(d) {
if (d.id === 'CHN')
return 'none';
// If fillKey - use that
// Otherwise check 'fill'
// Otherwise check 'defaultFill'
var fillColor;
var datum = colorCodeData[d.id];
if (datum && datum.fillKey) {
fillColor = fillData[val(datum.fillKey, {
data: colorCodeData[d.id],
geography: d
})];
}
if (typeof fillColor === 'undefined') {
fillColor = val(datum && datum.fillColor, fillData.defaultFill, {
data: colorCodeData[d.id],
geography: d
});
}
return fillColor;
})
.style('stroke-width', geoConfig.borderWidth)
.style('stroke-opacity', geoConfig.borderOpacity)
.style('stroke-dasharray', function(d) {
if (d.id.startsWith('CN.')) {
return '1,2'
} else
return '0';
})
.style('stroke', geoConfig.borderColor);
// var x = this.svg.select('g.datamaps-subunit CHN');
// x.style('stroke', 'white');
}
function handleGeographyConfig() {
var hoverover;
var svg = this.svg;
var self = this;
var options = this.options.geographyConfig;
if (options.highlightOnHover || options.popupOnHover) {
svg.selectAll('.datamaps-subunit')
.on('mouseover', function(d) {
var $this = d3.select(this);
var datum = self.options.data[d.id] || {};
if (options.highlightOnHover) {
var previousAttributes = {
'fill': $this.style('fill'),
'stroke': $this.style('stroke'),
'stroke-width': $this.style('stroke-width'),
'fill-opacity': $this.style('fill-opacity')
};
$this
.style('fill', val(datum.highlightFillColor, options.highlightFillColor, datum))
.style('stroke', val(datum.highlightBorderColor, options.highlightBorderColor, datum))
.style('stroke-width', val(datum.highlightBorderWidth, options.highlightBorderWidth, datum))
.style('stroke-opacity', val(datum.highlightBorderOpacity, options.highlightBorderOpacity, datum))
.style('fill-opacity', val(datum.highlightFillOpacity, options.highlightFillOpacity, datum))
.attr('data-previousAttributes', JSON.stringify(previousAttributes));
// As per discussion on https://github.com/markmarkoh/datamaps/issues/19
if (!/((MSIE)|(Trident))/.test(navigator.userAgent)) {
moveToFront.call(this);
}
}
if (options.popupOnHover) {
self.updatePopup($this, d, options, svg);
}
})
.on('mouseout', function() {
var $this = d3.select(this);
if (options.highlightOnHover) {
// Reapply previous attributes
var previousAttributes = JSON.parse($this.attr('data-previousAttributes'));
for (var attr in previousAttributes) {
$this.style(attr, previousAttributes[attr]);
}
}
$this.on('mousemove', null);
d3.selectAll('.datamaps-hoverover').style('display', 'none');
});
}
function moveToFront() {
this.parentNode.appendChild(this);
}
}
// Plugin to add a simple map legend
function addLegend(layer, data, options) {
data = data || {};
if (!this.options.fills) {
return;
}
var html = '<dl>';
var label = '';
if (data.legendTitle) {
html = '<h2>' + data.legendTitle + '</h2>' + html;
}
for (var fillKey in this.options.fills) {
if (fillKey === 'defaultFill') {
if (!data.defaultFillName) {
continue;
}
label = data.defaultFillName;
} else {
if (data.labels && data.labels[fillKey]) {
label = data.labels[fillKey];
} else {
label = fillKey + ': ';
}
}
html += '<dt>' + label + '</dt>';
html += '<dd style="background-color:' + this.options.fills[fillKey] + '"> </dd>';
}
html += '</dl>';
var hoverover = d3.select(this.options.element).append('div')
.attr('class', 'datamaps-legend')
.html(html);
}
function addGraticule(layer, options) {
var graticule = d3.geo.graticule();
this.svg.insert("path", '.datamaps-subunits')
.datum(graticule)
.attr("class", "datamaps-graticule")
.attr("d", this.path);
}
function handleArcs(layer, data, options) {
var self = this,
selected = null,
svg = this.svg;
if (!data || (data && !data.slice)) {
throw "Datamaps Error - arcs must be an array";
}
// For some reason arc options were put in an `options` object instead of the parent arc
// I don't like this, so to match bubbles and other plugins I'm moving it
// This is to keep backwards compatability
for (var i = 0; i < data.length; i++) {
data[i] = defaults(data[i], data[i].options);
delete data[i].options;
}
if (typeof options === "undefined") {
options = defaultOptions.arcConfig;
}
var arcs = layer.selectAll('path.datamaps-arc').data(data, JSON.stringify);
var path = d3.geo.path()
.projection(self.projection);
var longitudeCenter = self.projection.rotate()[0];
arcs
.enter()
.append('svg:path')
.attr('class', 'datamaps-arc')
.style('mix-blend-mode', 'screen')
.style('stroke-linecap', 'round')
// .style('stroke', function(datum) {
// return val(datum.strokeColor, options.strokeColor, datum);
// })
.style('fill', 'none')
// .style('stroke', 'rgba(254,153,0,0.5)')
.style('stroke', function(datum) {
// Check if the current browser is Firefox?
if (typeof InstallTrigger !== 'undefined') return 'rgba(254,153,0,0.5)';
// Otherwise return linearGradient;
var originXY = self.latLngToXY(val(datum.origin.latitude, datum), val(datum.origin.longitude, datum));
var destXY = self.latLngToXY(val(datum.destination.latitude, datum), val(datum.destination.longitude, datum));
if (Math.abs(originXY[0] - destXY[0]) > Math.abs(originXY[1] - destXY[1])) {
return (originXY[0] > destXY[0]) ? "url(#gradientLR)" : "url(#gradientRL)";
} else {
return (originXY[1] > destXY[1]) ? "url(#gradientTB)" : "url(#gradientBT)";
}
})
.style('stroke-width', function(datum) {
return val(datum.strokeWidth, options.strokeWidth, datum);
})
.attr('d', function(datum) {
var originXY, destXY;
if (typeof datum.origin === "string") {
originXY = self.path.centroid(svg.select('path.' + datum.origin).data()[0])
} else {
originXY = self.latLngToXY(val(datum.origin.latitude, datum), val(datum.origin.longitude, datum))
}
if (typeof datum.destination === 'string') {
destXY = self.path.centroid(svg.select('path.' + datum.destination).data()[0])
} else {
destXY = self.latLngToXY(val(datum.destination.latitude, datum), val(datum.destination.longitude, datum));
}
var midXY = [(originXY[0] + destXY[0]) / 2, (originXY[1] + destXY[1]) / 2];
if (options.greatArc) {
// TODO: Move this to inside `if` clause when setting attr `d`
var greatArc = d3.geo.greatArc()
.source(function(d) {
return [val(d.origin.longitude, d), val(d.origin.latitude, d)];
})
.target(function(d) {
return [val(d.destination.longitude, d), val(d.destination.latitude, d)];
});
return path(greatArc(datum))
}
var sharpness = val(datum.arcSharpness, options.arcSharpness, datum);
return "M" + originXY[0] + ',' + originXY[1] + "S" + (midXY[0] + (50 * sharpness)) + "," + (midXY[1] - (75 * sharpness)) + "," + destXY[0] + "," + destXY[1];
})
.attr('data-info', function(datum) {
return JSON.stringify(datum);
})
.on('mouseover', function(datum) {
var $this = d3.select(this);
if (options.popupOnHover) {
self.updatePopup($this, datum, options, svg);
}
})
.on('mouseout', function(datum) {
var $this = d3.select(this);
d3.selectAll('.datamaps-hoverover').style('display', 'none');
})
.transition()
.delay(100)
.style('fill', function(datum) {
/*
Thank you Jake Archibald, this is awesome.
Source: http://jakearchibald.com/2013/animated-line-drawing-svg/
*/
var length = this.getTotalLength();
this.style.transition = this.style.WebkitTransition = 'none';
// prevent dash when scaling
// this.style.strokeDasharray = length + ' ' + length;
// this.style.strokeDashoffset = length;
this.getBoundingClientRect();
this.style.transition = this.style.WebkitTransition = 'stroke-dashoffset ' + val(datum.animationSpeed, options.animationSpeed, datum) + 'ms ease-out';
this.style.strokeDashoffset = '0';
return 'none';
})
arcs.exit()
.transition()
.style('opacity', 0)
.remove();
}
function handleLabels(layer, options) {
var self = this;
options = options || {};
var labelStartCoodinates = this.projection([-67.707617, 42.722131]);
this.svg.selectAll(".datamaps-subunit")
.attr("data-foo", function(d) {
var center = self.path.centroid(d);
var xOffset = 7.5,
yOffset = 5;
if (["FL", "KY", "MI"].indexOf(d.id) > -1) xOffset = -2.5;
if (d.id === "NY") xOffset = -1;
if (d.id === "MI") yOffset = 18;
if (d.id === "LA") xOffset = 13;
var x, y;
x = center[0] - xOffset;
y = center[1] + yOffset;
var smallStateIndex = ["VT", "NH", "MA", "RI", "CT", "NJ", "DE", "MD", "DC"].indexOf(d.id);
if (smallStateIndex > -1) {
var yStart = labelStartCoodinates[1];
x = labelStartCoodinates[0];
y = yStart + (smallStateIndex * (2 + (options.fontSize || 12)));
layer.append("line")
.attr("x1", x - 3)
.attr("y1", y - 5)
.attr("x2", center[0])
.attr("y2", center[1])
.style("stroke", options.labelColor || "#000")
.style("stroke-width", options.lineWidth || 1)
}
layer.append("text")
.attr("x", x)
.attr("y", y)
.style("font-size", (options.fontSize || 10) + 'px')
.style("font-family", options.fontFamily || "Verdana")
.style("fill", options.labelColor || "#000")
.text(function() {
if (options.customLabelText && options.customLabelText[d.id]) {
return options.customLabelText[d.id]
} else {
return d.id
}
});
return "bar";
});
}
function handleBubbles(layer, data, options) {
var self = this,
fillData = this.options.fills,
filterData = this.options.filters,
svg = this.svg;
if (!data || (data && !data.slice)) {
throw "Datamaps Error - bubbles must be an array";
}
var bubbles = layer.selectAll('circle.datamaps-bubble').data(data, options.key);
bubbles
.enter()
.append('svg:circle')
.attr('class', 'datamaps-bubble')
.style('mix-blend-mode', 'screen')
.attr('cx', function(datum) {
var latLng;
if (datumHasCoords(datum)) {
latLng = self.latLngToXY(datum.latitude, datum.longitude);
} else if (datum.centered) {
if (datum.centered === 'USA') {
latLng = self.projection([-98.58333, 39.83333])
} else {
latLng = self.path.centroid(svg.select('path.' + datum.centered).data()[0]);
}
}
if (latLng) return latLng[0];
})
.attr('cy', function(datum) {
var latLng;
if (datumHasCoords(datum)) {
latLng = self.latLngToXY(datum.latitude, datum.longitude);
} else if (datum.centered) {
if (datum.centered === 'USA') {
latLng = self.projection([-98.58333, 39.83333])
} else {
latLng = self.path.centroid(svg.select('path.' + datum.centered).data()[0]);
}
}
if (latLng) return latLng[1];
})
.attr('r', function(datum) {
// If animation enabled start with radius 0, otherwise use full size.
return options.animate ? 0 : val(datum.radius, options.radius, datum);
})
.attr('data-info', function(datum) {
return JSON.stringify(datum);
})
.attr('filter', function(datum) {
var filterKey = filterData[val(datum.filterKey, options.filterKey, datum)];
if (filterKey) {
return filterKey;
}
})
.style('cursor', 'pointer')
.style('stroke', function(datum) {
return val(datum.borderColor, options.borderColor, datum);
})
.style('stroke-width', function(datum) {
return val(datum.borderWidth, options.borderWidth, datum);
// return "RGBA(255, 205, 150, 1.00)";
})
.style('stroke-opacity', function(datum) {
return 1;
// return val(datum.borderOpacity, options.borderOpacity, datum);
})
.style('fill-opacity', function(datum) {
return val(datum.fillOpacity, options.fillOpacity, datum);
// return 0.5;
})
.style('fill', function(datum) {
return "#FE9900";
// var fillColor = fillData[ val(datum.fillKey, options.fillKey, datum) ];
// return fillColor || fillData.defaultFill;
})
.on('mouseover', function(datum) {
var $this = d3.select(this);
if (options.highlightOnHover) {
// Save all previous attributes for mouseout
var previousAttributes = {
'fill': $this.style('fill'),
'stroke': $this.style('stroke'),
'stroke-width': $this.style('stroke-width'),
'fill-opacity': $this.style('fill-opacity')
};
$this
.style('fill', val(datum.highlightFillColor, options.highlightFillColor, datum))
.style('stroke', val(datum.highlightBorderColor, options.highlightBorderColor, datum))
.style('stroke-width', val(datum.highlightBorderWidth, options.highlightBorderWidth, datum))
.style('stroke-opacity', val(datum.highlightBorderOpacity, options.highlightBorderOpacity, datum))
.style('fill-opacity', val(datum.highlightFillOpacity, options.highlightFillOpacity, datum))
.attr('data-previousAttributes', JSON.stringify(previousAttributes));
}
if (options.popupOnHover) {
self.updatePopup($this, datum, options, svg);
}
})
.on('mouseout', function(datum) {
var $this = d3.select(this);
if (options.highlightOnHover) {
// Reapply previous attributes
var previousAttributes = JSON.parse($this.attr('data-previousAttributes'));
for (var attr in previousAttributes) {
$this.style(attr, previousAttributes[attr]);
}
}
d3.selectAll('.datamaps-hoverover').style('display', 'none');
})
bubbles.transition()
.duration(400)
.attr('r', function(datum) {
return val(datum.radius, options.radius, datum);
})
.transition()
.duration(0)
.attr('data-info', function(d) {
return JSON.stringify(d);
});
bubbles.exit()
.transition()
.delay(options.exitDelay)
.attr("r", 0)
.remove();
function datumHasCoords(datum) {
return typeof datum !== 'undefined' && typeof datum.latitude !== 'undefined' && typeof datum.longitude !== 'undefined';
}
}
function defaults(obj) {
Array.prototype.slice.call(arguments, 1).forEach(function(source) {
if (source) {
for (var prop in source) {
// Deep copy if property not set
if (obj[prop] == null) {
if (typeof source[prop] == 'function') {
obj[prop] = source[prop];
} else {
obj[prop] = JSON.parse(JSON.stringify(source[prop]));
}
}
}
}
});
return obj;
}
/**************************************
Public Functions
***************************************/
function Datamap(options) {
if (typeof d3 === 'undefined' || typeof topojson === 'undefined') {
throw new Error('Include d3.js (v3.0.3 or greater) and topojson on this page before creating a new map');
}
// Set options for global use
this.options = defaults(options, defaultOptions);
this.options.geographyConfig = defaults(options.geographyConfig, defaultOptions.geographyConfig);
this.options.projectionConfig = defaults(options.projectionConfig, defaultOptions.projectionConfig);
this.options.bubblesConfig = defaults(options.bubblesConfig, defaultOptions.bubblesConfig);
this.options.arcConfig = defaults(options.arcConfig, defaultOptions.arcConfig);
// Add the SVG container
if (d3.select(this.options.element).select('svg').length > 0) {
addContainer.call(this, this.options.element, this.options.height, this.options.width);
}
// Add core plugins to this instance
this.addPlugin('bubbles', handleBubbles);
this.addPlugin('legend', addLegend);
this.addPlugin('arc', handleArcs);
this.addPlugin('labels', handleLabels);
this.addPlugin('graticule', addGraticule);
// Append style block with basic hoverover styles
if (!this.options.disableDefaultStyles) {
addStyleBlock();
}
return this.draw();
}
// Resize map
Datamap.prototype.resize = function() {
var self = this;
var options = self.options;
if (options.responsive) {
var newsize = options.element.clientWidth,
oldsize = d3.select(options.element).select('svg').attr('data-width');
d3.select(options.element).select('svg').selectAll('g').attr('transform', 'scale(' + (newsize / oldsize) + ')');
}
}
// Actually draw the features(states & countries)
Datamap.prototype.draw = function() {
// Save off in a closure
var self = this;
var options = self.options;
// Set projections and paths based on scope
var pathAndProjection = options.setProjection.apply(this, [options.element, options]);
this.path = pathAndProjection.path;
this.projection = pathAndProjection.projection;
// If custom URL for topojson data, retrieve it and render
if (options.geographyConfig.dataUrl) {
d3.json(options.geographyConfig.dataUrl, function(error, results) {
if (error) throw new Error(error);
self.customTopo = results;
draw(results);
});
} else {
draw(this[options.scope + 'Topo'] || options.geographyConfig.dataJson);
}
return this;
function draw(data) {
// If fetching remote data, draw the map first then call `updateChoropleth`
if (self.options.dataUrl) {
// Allow for csv or json data types
d3[self.options.dataType](self.options.dataUrl, function(data) {
// In the case of csv, transform data to object
if (self.options.dataType === 'csv' && (data && data.slice)) {
var tmpData = {};
for (var i = 0; i < data.length; i++) {
tmpData[data[i].id] = data[i];
}
data = tmpData;
}
Datamaps.prototype.updateChoropleth.call(self, data);
});
}
drawSubunits.call(self, data);
handleGeographyConfig.call(self);
if (self.options.geographyConfig.popupOnHover || self.options.bubblesConfig.popupOnHover) {
hoverover = d3.select(self.options.element).append('div')
.attr('class', 'datamaps-hoverover')
.style('z-index', 10001)
.style('position', 'absolute');
}
// Fire off finished callback
self.options.done(self);
}
};
/**************************************
TopoJSON
***************************************/
Datamap.prototype.worldTopo = {
"type": "Topology",
"objects": {
"chn": {
"type": "GeometryCollection",
"geometries": [{
"type": "Polygon",
"id": "CN.GS",
"arcs": [
[0, 1, 2, 3, 4, 5, 6]
]
}, {
"type": "Polygon",
"id": "CN.QH",
"arcs": [
[7, 8, 9, -4]
]
}, {
"type": "MultiPolygon",
"id": "CN.GX",
"arcs": [
[
[10]
],
[
[11]
],
[
[12]
],
[
[13, 14, 15, 16, 17]
]
]
}, {
"type": "Polygon",
"id": "CN.GZ",
"arcs": [
[18, -17, 19, 20, 21]
]
}, {
"type": "Polygon",
"id": "CN.CQ",
"arcs": [
[22, 23, -22, 24, 25]
]
}, {
"type": "Polygon",
"id": "CN.BJ",
"arcs": [
[26, 27, 28, 29]
]
}, {
"type": "MultiPolygon",
"id": "CN.FJ",
"arcs": [
[
[30]
],
[
[31]
],
[
[32]
],
[
[33]
],
[
[34]
],
[
[35]
],
[
[36]
],
[
[37]
],
[
[38]
],
[
[39]
],
[
[40]
],
[
[41]
],
[
[42]
],
[
[43]
],
[
[44]
],
[
[45]
],
[
[46]
],
[
[47]
],
[
[48, 49, 50, 51]
]
]
}, {
"type": "Polygon",
"id": "CN.AH",
"arcs": [
[52, 53, 54, 55, 56, 57]
]
}, {
"type": "MultiPolygon",
"id": "CN.GD",
"arcs": [
[
[58]
],
[
[59]
],
[
[60]
],
[
[61]
],
[
[62]
],
[
[63]
],
[
[64]
],
[
[65]
],
[
[66]
],
[
[67]
],
[
[68]
],
[
[69]
],
[
[70]
],
[
[71]
],
[
[72]
],
[
[73]
],
[
[74]
],
[
[75]
],
[
[76]
],
[
[77]
],
[
[78]
],
[
[79]
],
[
[80]
],
[
[81, -50, 82, -14, 83]
]
]
}, {
"type": "Polygon",
"id": "CN.XZ",
"arcs": [
[-9, 84, 85, 86, 87]
]
}, {
"type": "Polygon",
"id": "CN.XJ",
"arcs": [
[-5, -10, -88, 88]
]
}, {
"type": "Polygon",
"id": "CN.HA",
"arcs": [
[89]
]
}, {
"type": "Polygon",
"id": "CN.NX",
"arcs": [
[90, -1, 91]
]
}, {
"type": "Polygon",
"id": "CN.SA",
"arcs": [
[92, 93, 94, -26, 95, -2, -91, 96]
]
}, {
"type": "Polygon",
"id": "CN.SX",
"arcs": [
[97, -93, 98, 99]
]
}, {
"type": "Polygon",
"id": "CN.HU",
"arcs": [
[-55, 100, 101, -23, -95, 102]
]
}, {
"type": "Polygon",
"id": "CN.HN",
"arcs": [
[103, -84, -18, -19, -24, -102]
]
}, {
"type": "Polygon",
"id": "CN.SC",
"arcs": [
[-96, -25, -21, 104, -85, -8, -3]
]
}, {
"type": "Polygon",
"id": "CN.YN",
"arcs": [
[-20, -16, 105, -86, -105]
]
}, {
"type": "MultiPolygon",
"id": "CN.HB",
"arcs": [
[
[106]
],
[
[-28, 107]
],
[
[108, 109, 110, -30, 111, 112, 113, 114, -100, 115]
]
]
}, {
"type": "Polygon",
"id": "CN.HE",
"arcs": [
[116, -56, -103, -94, -98, -115]
]
}, {
"type": "MultiPolygon",
"id": "CN.LN",
"arcs": [
[
[117]
],
[
[118]
],
[
[119]
],
[
[120]
],
[
[121]
],
[
[122]
],
[
[123]
],
[
[124]
],
[
[125]
],
[
[126]
],
[
[127]
],
[
[128, -109, 129, 130, 131]
]
]
}, {
"type": "MultiPolygon",
"id": "CN.SD",
"arcs": [
[
[132]
],
[
[133]
],
[
[134]
],
[
[135]
],
[
[136]
],
[
[137]
],
[
[138, -57, -117, -114, 139]
],
[
[140]
],
[
[141]
]
]
}, {
"type": "MultiPolygon",
"id": "CN.TJ",
"arcs": [
[
[142]
],
[
[143]
],
[
[144]
],
[
[145, -112, -29, -108, -27, -111]
]
]
}, {
"type": "Polygon",
"id": "CN.JX",
"arcs": [
[146, -51, -82, -104, -101, -54]
]
}, {
"type": "MultiPolygon",
"id": "CN.JS",
"arcs": [
[
[147]
],
[
[148]
],
[
[149]
],
[
[150, 151, -58, -139, 152]
]
]
}, {
"type": "MultiPolygon",
"id": "CN.SH",
"arcs": [
[
[153]
],
[
[154]
],
[
[155, 156, -151]
],
[
[157]
]
]
}, {
"type": "MultiPolygon",
"id": "CN.ZJ",
"arcs": [
[
[158]
],
[
[159]
],
[
[160]
],
[
[161]
],
[
[162]
],
[
[163]
],
[
[164]
],
[
[165]
],
[
[166]
],
[
[167]
],
[
[168]
],
[
[169]
],
[
[170]
],
[
[171]
],
[
[172]
],
[
[173]
],
[
[174]
],
[
[175]
],
[
[176]
],
[
[177]
],
[
[178]
],
[
[179]
],
[
[180]
],
[
[181]
],
[
[182]
],
[
[183]
],
[
[184]
],
[
[185]
],
[
[186]
],
[
[187]
],
[
[188]
],
[
[189]
],
[
[190]
],
[
[191]
],
[
[192]
],
[
[193]
],
[
[194]
],
[
[-157, 195, -52, -147, -53, -152]
]
]
}, {
"type": "Polygon",
"id": "CN.JL",
"arcs": [
[196, 131, -132, -131, 197, 198]
]
}, {
"type": "Polygon",
"id": "CN.NM",
"arcs": [
[199, -198, -130, -116, -99, -97, -92, -7, 200]
]
}, {
"type": "Polygon",
"id": "CN.HL",
"arcs": [
[-199, -200, 201]
]
}, {
"type": "MultiPolygon",
"id": "CN",
"arcs": [
[
[202]
],
[
[203]
],
[
[204]
],
[
[205]
],
[
[206]
],
[
[207]
],
[
[208]
],
[
[209]
]
]
}]
},
"world": {
"type": "GeometryCollection",
"geometries": [{
"type": "Polygon",
"id": "AFG",
"arcs": [
[210, 211, 212, 213, 214, 215]
]
}, {
"type": "MultiPolygon",
"id": "AGO",
"arcs": [
[
[216, 217, 218, 219]
],
[
[220, 221, 222]
]
]
}, {
"type": "Polygon",
"id": "ALB",
"arcs": [
[223, 224, 225, 226, 227]
]
}, {
"type": "Polygon",
"id": "ARE",
"arcs": [
[228, 229, 230, 231, 232]
]
}, {
"type": "MultiPolygon",
"id": "ARG",
"arcs": [
[
[233, 234]
],
[
[235, 236, 237, 238, 239, 240]
]
]
}, {
"type": "Polygon",
"id": "ARM",
"arcs": [
[241, 242, 243, 244, 245]
]
}, {
"type": "MultiPolygon",
"id": "ATA",
"arcs": [
[
[246]
],
[
[247]
],
[
[248]
],
[
[249]
],
[
[250]
],
[
[251]
],
[
[252]
],
[
[253]
]
]
}, {
"type": "Polygon",
"id": "ATF",
"arcs": [
[254]
]
}, {
"type": "MultiPolygon",
"id": "AUS",
"arcs": [
[
[255]
],
[
[256]
]
]
}, {
"type": "Polygon",
"id": "AUT",
"arcs": [
[257, 258, 259, 260, 261, 262, 263]
]
}, {
"type": "MultiPolygon",
"id": "AZE",
"arcs": [
[
[264, -245]
],
[
[265, 266, -243, 267, 268]
]
]
}, {
"type": "Polygon",
"id": "BDI",
"arcs": [
[269, 270, 271]
]
}, {
"type": "Polygon",
"id": "BEL",
"arcs": [
[272, 273, 274, 275, 276]
]
}, {
"type": "Polygon",
"id": "BEN",
"arcs": [
[277, 278, 279, 280, 281]
]
}, {
"type": "Polygon",
"id": "BFA",
"arcs": [
[282, 283, 284, -280, 285, 286]
]
}, {
"type": "Polygon",
"id": "BGD",
"arcs": [
[287, 288, 289]
]
}, {
"type": "Polygon",
"id": "BGR",
"arcs": [
[290, 291, 292, 293, 294, 295]
]
}, {
"type": "MultiPolygon",
"id": "BHS",
"arcs": [
[
[296]
],
[
[297]
],
[
[298]
]
]
}, {
"type": "Polygon",
"id": "BIH",
"arcs": [
[299, 300, 301]
]
}, {
"type": "Polygon",
"id": "BLR",
"arcs": [
[302, 303, 304, 305, 306]
]
}, {
"type": "Polygon",
"id": "BLZ",
"arcs": [
[307, 308, 309]
]
}, {
"type": "Polygon",
"id": "BOL",
"arcs": [
[310, 311, 312, 313, -241]
]
}, {
"type": "Polygon",
"id": "BRA",
"arcs": [
[-237, 314, -313, 315, 316, 317, 318, 319, 320, 321, 322]
]
}, {
"type": "Polygon",
"id": "BRN",
"arcs": [
[323, 324]
]
}, {
"type": "Polygon",
"id": "BTN",
"arcs": [
[325, 326]
]
}, {
"type": "Polygon",
"id": "BWA",
"arcs": [
[327, 328, 329, 330]
]
}, {
"type": "Polygon",
"id": "CAF",
"arcs": [
[331, 332, 333, 334, 335, 336, 337]
]
}, {
"type": "MultiPolygon",
"id": "CAN",
"arcs": [
[
[338]
],
[
[339]
],
[
[340]
],
[
[341]
],
[
[342]
],
[
[343]
],
[
[344]
],
[
[345]
],
[
[346]
],
[
[347]
],
[
[348, 349, 350, 351]
],
[
[352]
],
[
[353]
],
[
[354]
],
[
[355]
],
[
[356]
],
[
[357]
],
[
[358]
],
[
[359]
],
[
[360]
],
[
[361]
],
[
[362]
],
[
[363]
],
[
[364]
],
[
[365]
],
[
[366]
],
[
[367]
],
[
[368]
],
[
[369]
],
[
[370]
]
]
}, {
"type": "Polygon",
"id": "CHE",
"arcs": [
[-261, 371, 372, 373]
]
}, {
"type": "MultiPolygon",
"id": "CHL",
"arcs": [
[
[-234, 374]
],
[
[-240, 375, 376, -311]
]
]
}, {
"type": "MultiPolygon",
"id": "CHN",
"arcs": [
[
[377]
],
[
[378, 379, 380, 381, 382, 383, -327, 384, 385, 386, 387, -214, 388, 389, 390, 391, 392, 393]
]
]
}, {
"type": "Polygon",
"id": "CIV",
"arcs": [
[394, 395, 396, 397, -283, 398]
]
}, {
"type": "Polygon",
"id": "CMR",
"arcs": [
[399, 400, 401, 402, 403, 404, -338, 405]
]
}, {
"type": "Polygon",
"id": "COD",
"arcs": [
[406, 407, -270, 408, 409, -220, 410, -223, 411, -336, 412]
]
}, {
"type": "Polygon",
"id": "COG",
"arcs": [
[-222, 413, 414, -406, -337, -412]
]
}, {
"type": "Polygon",
"id": "COL",
"arcs": [
[415, 416, 417, 418, 419, -317, 420]
]
}, {
"type": "Polygon",
"id": "CRI",
"arcs": [
[421, 422, 423, 424]
]
}, {
"type": "Polygon",
"id": "CUB",
"arcs": [
[425]
]
}, {
"type": "Polygon",
"id": "-99",
"arcs": [
[426, 427]
]
}, {
"type": "Polygon",
"id": "CYP",
"arcs": [
[428, -428]
]
}, {
"type": "Polygon",
"id": "CZE",
"arcs": [
[-263, 429, 430, 431]
]
}, {
"type": "Polygon",
"id": "DEU",
"arcs": [
[432, 433, -430, -262, -374, 434, 435, -274, 436, 437, 438]
]
}, {
"type": "Polygon",
"id": "DJI",
"arcs": [
[439, 440, 441, 442]
]
}, {
"type": "MultiPolygon",
"id": "DNK",
"arcs": [
[
[443]
],
[
[-439, 444]
]
]
}, {
"type": "Polygon",
"id": "DOM",
"arcs": [
[445, 446]
]
}, {
"type": "Polygon",
"id": "DZA",
"arcs": [
[447, 448, 449, 450, 451, 452, 453, 454]
]
}, {
"type": "Polygon",
"id": "ECU",
"arcs": [
[455, -416, 456]
]
}, {
"type": "Polygon",
"id": "EGY",
"arcs": [
[457, 458, 459, 460, 461]
]
}