<!doctype html>
<html class="zc-html">
<head>
<meta charset="utf-8">
<title>ZingSoft Demo</title>
<script nonce="undefined" src="https://cdn.zingchart.com/zingchart.min.js"></script>
<style>
.chart--container {
height: 100%;
width: 100%;
min-height: 530px;
}
.zc-ref {
display: none;
}
</style>
</head>
<body class="zc-body">
<div id="myChart" class="chart--container">
<a href="https://www.zingchart.com/" rel="noopener" class="zc-ref">Powered by ZingChart</a>
</div>
<script>
ZC.LICENSE = ["569d52cefae586f634c54f86dc99e6a9", "b55b025e438fa8a98e32482b5f768ff5"]; // CHART CONFIG
// -----------------------------
let chartConfig = {
title: {
text: 'Brazil\'s 25 Most Populated Cities',
align: 'left',
fontSize: '14px'
},
shapes: [{
type: 'zingchart.maps',
options: {
name: 'bra',
offsetX: '-50px',
offsetY: '35px',
style: {
backgroundColor: '#4caf50',
controls: {
placement: 'br'
},
hoverState: {
visible: false
},
label: {
fontSize: '8px',
visible: true
},
tooltip: {
backgroundColor: '#ff9800',
borderWidth: '0px',
fontColor: '#FFF',
fontSize: '18px'
},
}
}
}]
};
// DEFINE DATA
// -----------------------------
// example format of data for cities
let oCities = {
'Sao Paulo': {
iPopulation: 11895893,
sPopulation: '11,895,893',
latitude: -23.5505,
longitude: -46.6333,
name: 'São Paulo',
x: '0px',
y: '0px'
},
'Rio de Janeiro': {
iPopulation: 6453682,
sPopulation: '6,453,682',
latitude: -22.9068,
longitude: -43.1729,
name: 'Rio de Janeiro',
x: '0px',
y: '0px'
},
'Salvador': {
iPopulation: 2902927,
sPopulation: '2,902,927',
latitude: -12.9722,
longitude: -38.5014,
name: 'Salvador',
x: '0px',
y: '0px'
},
'Brasilia': {
iPopulation: 2852372,
sPopulation: '2,852,372',
latitude: -15.7942,
longitude: -47.8822,
name: 'Brasília',
x: '0px',
y: '0px'
},
'Fortaleza': {
iPopulation: 2571896,
sPopulation: '2,571,896',
latitude: -3.7319,
longitude: -38.5267,
name: 'Fortaleza',
x: '0px',
y: '0px'
},
'Belo Horizonte': {
iPopulation: 2491109,
sPopulation: '2,491,109',
latitude: -19.9245,
longitude: -43.9352,
name: 'Belo Horizonte',
x: '0px',
y: '0px'
},
'Porto Alegre': {
iPopulation: 1472482,
sPopulation: '1,472,482',
latitude: -30.0347,
longitude: -51.2177,
name: 'Porto Alegre',
x: '0px',
y: '0px'
},
'Sao Luis': {
iPopulation: 1064197,
sPopulation: '1,064,197',
latitude: -2.5391,
longitude: -44.2829,
name: 'São Luís',
x: '0px',
y: '0px'
},
'Teresina': {
iPopulation: 840600,
sPopulation: '840,600',
latitude: -5.0920,
longitude: -42.8038,
name: 'Teresina',
x: '0px',
y: '0px'
},
'Contagem': {
iPopulation: 643476,
sPopulation: '643,476',
latitude: -19.9162,
longitude: -44.0809,
name: 'Contagem',
x: '0px',
y: '0px'
},
'Diadema': {
iPopulation: 409613,
sPopulation: '409,613',
latitude: -23.6818,
longitude: -46.6210,
name: 'Diadema',
x: '0px',
y: '0px'
},
'Manaus': {
iPopulation: 2020301,
sPopulation: '2,020,301',
latitude: -3.1190,
longitude: -60.0217,
name: 'Manaus',
x: '0px',
y: '0px'
},
'Curitiba': {
iPopulation: 1864416,
sPopulation: '1,864,416',
latitude: -25.4244,
longitude: -49.2654,
name: 'Curitiba',
x: '0px',
y: '0px'
},
'Recife': {
iPopulation: 1608488,
sPopulation: '1,608,488',
latitude: -8.0476,
longitude: -34.8770,
name: 'Recife',
x: '0px',
y: '0px'
},
'Belem': {
iPopulation: 1432844,
sPopulation: '1,432,844',
latitude: -1.4558,
longitude: -48.4902,
name: 'Belém',
x: '0px',
y: '0px'
},
'Goiania': {
iPopulation: 1424364,
sPopulation: '1,424,364',
latitude: -16.6869,
longitude: -49.2648,
name: 'Goiânia',
x: '0px',
y: '0px'
},
'Guarulhos': {
iPopulation: 1312197,
sPopulation: '1,312,197',
latitude: -23.4543,
longitude: -46.5338,
name: 'Guarulhos',
x: '0px',
y: '0px'
},
'Campinas': {
iPopulation: 1154617,
sPopulation: '1,154,617',
latitude: -22.9099,
longitude: -47.0626,
name: 'Campinas',
x: '0px',
y: '0px'
},
'Sao Goncalo': {
iPopulation: 1031903,
sPopulation: '1,031,903',
latitude: -22.8272,
longitude: -43.0638,
name: 'São Gonçalo',
x: '0px',
y: '0px'
},
'Maceio': {
iPopulation: 1005319,
sPopulation: '1,005,319',
latitude: -9.6499,
longitude: -35.7089,
name: 'Maceio',
x: '0px',
y: '0px'
},
'Duque de Caxias': {
iPopulation: 878402,
sPopulation: '878,402',
latitude: -22.7863,
longitude: -43.3053,
name: 'Duque de Caxias',
x: '0px',
y: '0px'
},
'Natal': {
iPopulation: 862044,
sPopulation: '862,044',
latitude: -5.7793,
longitude: -35.2009,
name: 'Natal',
x: '0px',
y: '0px'
},
'Campo Grande': {
iPopulation: 853622,
sPopulation: '853,622',
latitude: -20.4698,
longitude: -54.6202,
name: 'Campo Grande',
x: '0px',
y: '0px'
},
'Sao Bernardo do Campo': {
iPopulation: 811489,
sPopulation: '811,489',
latitude: -23.6898,
longitude: -46.5648,
name: 'São Bernardo do Campo',
x: '0px',
y: '0px'
},
'Nova Iguacu': {
iPopulation: 806177,
sPopulation: '806,177',
latitude: -22.7561,
longitude: -43.4607,
name: 'Nova Iguaçu',
x: '0px',
y: '0px'
},
'Joao Pessoa': {
iPopulation: 780738,
sPopulation: '780,738',
latitude: -7.1195,
longitude: -34.8449,
name: 'João Pessoa',
x: '0px',
y: '0px'
},
'Santo Andre': {
iPopulation: 707613,
sPopulation: '707,613',
latitude: -23.6742,
longitude: -46.5436,
name: 'Santo André',
x: '0px',
y: '0px'
}
};
let defaultColor = '#ffc107';
// HELPER METHODS
// -----------------------------
function drawCity(city) {
return {
type: 'circle',
id: city,
tooltip: {
text: buildTooltipText(city),
padding: '10px',
backgroundColor: '#FFF',
borderColor: (oCities[city].color) ? oCities[city].color : defaultColor,
borderRadius: '5px',
borderWidth: '2px',
fontSize: '14px',
textAlign: 'left'
},
alpha: .6,
backgroundColor: (oCities[city].color) ? oCities[city].color : defaultColor,
borderWidth: '3px',
borderColor: 'none',
hoverState: {
alpha: .1,
borderAlpha: 1,
borderColor: (oCities[city].color) ? oCities[city].color : defaultColor,
size: calculateCircleSize(oCities[city].iPopulation) + 2
},
map: 'bra',
size: calculateCircleSize(oCities[city].iPopulation),
x: oCities[city].x,
y: oCities[city].y
}
}
/**
* Code below calculates bubble size
*/
function buildTooltipText(city) {
return '<b>Population 2016</b> ??<br> ' + oCities[city].name + ': ' + oCities[city].sPopulation;
}
function calculateCircleSize(population) {
let range = [5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45];
let domain = generateDomain('quantize', [400000, 12000000], range);
let keyValue = quantize(population, domain, range);
return keyValue.range;
}
function leftOrRight(value, left, right) {
let leftDiff = Math.abs(value - left);
let rightDiff = Math.abs(value - right);
return leftDiff < rightDiff ? true : false;
}
function isBetween(value, left, right) {
return value > left && value < right;
}
function quantizeIndex(value, scale) {
let first = scale[0];
let last = scale[scale.length - 1];
for (let i = 0; i < scale.length; i++) {
if (value <= first) {
return 0;
} else if (value >= last) {
return scale.length - 1;
} else if (i < scale.length - 1) {
if (value == scale[i]) {
return i;
} else {
let current = scale[i];
let next = scale[i + 1];
if (isBetween(value, current, next)) {
return leftOrRight(value, current, next) ? i : i + 1;
}
}
}
}
}
// define mix max for domain
function quantize(value, domain, range) {
let index = quantizeIndex(value, domain);
return {
range: range[index],
domain: domain[index]
}
}
function quantizeDomain(aDomain, aRange) {
let iMin = aDomain[0];
let iMax = aDomain[1];
let iSlope = (iMax - iMin) / (aRange.length - 1);
let aScale = [];
for (let i = 0; i < aRange.length; i++) {
aScale[i] = (iSlope * i + iMin);
}
return aScale;
}
function generateDomain(sType, aDomain, aRange) {
let aScale = aDomain;
if (sType == 'quantize') {
aScale = quantizeDomain(aDomain, aRange)
}
return aScale;
}
for (let city in oCities) {
// save xy position
oCities[city].x = oCities[city].longitude + 'lon';
oCities[city].y = oCities[city].latitude + 'lat';
chartConfig.shapes.push(drawCity(city));
}
// RENDER CHARTS
// -----------------------------
zingchart.loadModules('maps,maps-bra');
zingchart.render({
id: 'myChart',
data: chartConfig
});
</script>
</body>
</html>
// CHART CONFIG
// -----------------------------
let chartConfig = {
title: {
text: 'Brazil\'s 25 Most Populated Cities',
align: 'left',
fontSize: '14px'
},
shapes: [
{
type: 'zingchart.maps',
options: {
name: 'bra',
offsetX: '-50px',
offsetY: '35px',
style: {
backgroundColor: '#4caf50',
controls: {
placement: 'br'
},
hoverState: {
visible: false
},
label: {
fontSize: '8px',
visible: true
},
tooltip: {
backgroundColor: '#ff9800',
borderWidth: '0px',
fontColor: '#FFF',
fontSize: '18px'
},
}
}
}
]
};
// DEFINE DATA
// -----------------------------
// example format of data for cities
let oCities = {
'Sao Paulo': {
iPopulation: 11895893,
sPopulation: '11,895,893',
latitude: -23.5505,
longitude: -46.6333,
name: 'São Paulo',
x: '0px',
y: '0px'
},
'Rio de Janeiro': {
iPopulation: 6453682,
sPopulation: '6,453,682',
latitude: -22.9068,
longitude: -43.1729,
name: 'Rio de Janeiro',
x: '0px',
y: '0px'
},
'Salvador': {
iPopulation: 2902927,
sPopulation: '2,902,927',
latitude: -12.9722,
longitude: -38.5014,
name: 'Salvador',
x: '0px',
y: '0px'
},
'Brasilia': {
iPopulation: 2852372,
sPopulation: '2,852,372',
latitude: -15.7942,
longitude: -47.8822,
name: 'Brasília',
x: '0px',
y: '0px'
},
'Fortaleza': {
iPopulation: 2571896,
sPopulation: '2,571,896',
latitude: -3.7319,
longitude: -38.5267,
name: 'Fortaleza',
x: '0px',
y: '0px'
},
'Belo Horizonte': {
iPopulation: 2491109,
sPopulation: '2,491,109',
latitude: -19.9245,
longitude: -43.9352,
name: 'Belo Horizonte',
x: '0px',
y: '0px'
},
'Porto Alegre': {
iPopulation: 1472482,
sPopulation: '1,472,482',
latitude: -30.0347,
longitude: -51.2177,
name: 'Porto Alegre',
x: '0px',
y: '0px'
},
'Sao Luis': {
iPopulation: 1064197,
sPopulation: '1,064,197',
latitude: -2.5391,
longitude: -44.2829,
name: 'São Luís',
x: '0px',
y: '0px'
},
'Teresina': {
iPopulation: 840600,
sPopulation: '840,600',
latitude: -5.0920,
longitude: -42.8038,
name: 'Teresina',
x: '0px',
y: '0px'
},
'Contagem': {
iPopulation: 643476,
sPopulation: '643,476',
latitude: -19.9162,
longitude: -44.0809,
name: 'Contagem',
x: '0px',
y: '0px'
},
'Diadema': {
iPopulation: 409613,
sPopulation: '409,613',
latitude: -23.6818,
longitude: -46.6210,
name: 'Diadema',
x: '0px',
y: '0px'
},
'Manaus': {
iPopulation: 2020301,
sPopulation: '2,020,301',
latitude: -3.1190,
longitude: -60.0217,
name: 'Manaus',
x: '0px',
y: '0px'
},
'Curitiba': {
iPopulation: 1864416,
sPopulation: '1,864,416',
latitude: -25.4244,
longitude: -49.2654,
name: 'Curitiba',
x: '0px',
y: '0px'
},
'Recife': {
iPopulation: 1608488,
sPopulation: '1,608,488',
latitude: -8.0476,
longitude: -34.8770,
name: 'Recife',
x: '0px',
y: '0px'
},
'Belem': {
iPopulation: 1432844,
sPopulation: '1,432,844',
latitude: -1.4558,
longitude: -48.4902,
name: 'Belém',
x: '0px',
y: '0px'
},
'Goiania': {
iPopulation: 1424364,
sPopulation: '1,424,364',
latitude: -16.6869,
longitude: -49.2648,
name: 'Goiânia',
x: '0px',
y: '0px'
},
'Guarulhos': {
iPopulation: 1312197,
sPopulation: '1,312,197',
latitude: -23.4543,
longitude: -46.5338,
name: 'Guarulhos',
x: '0px',
y: '0px'
},
'Campinas': {
iPopulation: 1154617,
sPopulation: '1,154,617',
latitude: -22.9099,
longitude: -47.0626,
name: 'Campinas',
x: '0px',
y: '0px'
},
'Sao Goncalo': {
iPopulation: 1031903,
sPopulation: '1,031,903',
latitude: -22.8272,
longitude: -43.0638,
name: 'São Gonçalo',
x: '0px',
y: '0px'
},
'Maceio': {
iPopulation: 1005319,
sPopulation: '1,005,319',
latitude: -9.6499,
longitude: -35.7089,
name: 'Maceio',
x: '0px',
y: '0px'
},
'Duque de Caxias': {
iPopulation: 878402,
sPopulation: '878,402',
latitude: -22.7863,
longitude: -43.3053,
name: 'Duque de Caxias',
x: '0px',
y: '0px'
},
'Natal': {
iPopulation: 862044,
sPopulation: '862,044',
latitude: -5.7793,
longitude: -35.2009,
name: 'Natal',
x: '0px',
y: '0px'
},
'Campo Grande': {
iPopulation: 853622,
sPopulation: '853,622',
latitude: -20.4698,
longitude: -54.6202,
name: 'Campo Grande',
x: '0px',
y: '0px'
},
'Sao Bernardo do Campo': {
iPopulation: 811489,
sPopulation: '811,489',
latitude: -23.6898,
longitude: -46.5648,
name: 'São Bernardo do Campo',
x: '0px',
y: '0px'
},
'Nova Iguacu': {
iPopulation: 806177,
sPopulation: '806,177',
latitude: -22.7561,
longitude: -43.4607,
name: 'Nova Iguaçu',
x: '0px',
y: '0px'
},
'Joao Pessoa': {
iPopulation: 780738,
sPopulation: '780,738',
latitude: -7.1195,
longitude: -34.8449,
name: 'João Pessoa',
x: '0px',
y: '0px'
},
'Santo Andre': {
iPopulation: 707613,
sPopulation: '707,613',
latitude: -23.6742,
longitude: -46.5436,
name: 'Santo André',
x: '0px',
y: '0px'
}
};
let defaultColor = '#ffc107';
// HELPER METHODS
// -----------------------------
function drawCity(city) {
return {
type: 'circle',
id: city,
tooltip: {
text: buildTooltipText(city),
padding: '10px',
backgroundColor: '#FFF',
borderColor: (oCities[city].color) ? oCities[city].color : defaultColor,
borderRadius: '5px',
borderWidth: '2px',
fontSize: '14px',
textAlign: 'left'
},
alpha: .6,
backgroundColor: (oCities[city].color) ? oCities[city].color : defaultColor,
borderWidth: '3px',
borderColor: 'none',
hoverState: {
alpha: .1,
borderAlpha: 1,
borderColor: (oCities[city].color) ? oCities[city].color : defaultColor,
size: calculateCircleSize(oCities[city].iPopulation) + 2
},
map: 'bra',
size: calculateCircleSize(oCities[city].iPopulation),
x: oCities[city].x,
y: oCities[city].y
}
}
/**
* Code below calculates bubble size
*/
function buildTooltipText(city) {
return '<b>Population 2016</b> ??<br> ' + oCities[city].name + ': ' + oCities[city].sPopulation;
}
function calculateCircleSize(population) {
let range = [5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45];
let domain = generateDomain('quantize', [400000, 12000000], range);
let keyValue = quantize(population, domain, range);
return keyValue.range;
}
function leftOrRight(value, left, right) {
let leftDiff = Math.abs(value - left);
let rightDiff = Math.abs(value - right);
return leftDiff < rightDiff ? true : false;
}
function isBetween(value, left, right) {
return value > left && value < right;
}
function quantizeIndex(value, scale) {
let first = scale[0];
let last = scale[scale.length - 1];
for (let i = 0; i < scale.length; i++) {
if (value <= first) {
return 0;
}
else if (value >= last) {
return scale.length - 1;
}
else if (i < scale.length - 1) {
if (value == scale[i]) {
return i;
}
else {
let current = scale[i];
let next = scale[i + 1];
if (isBetween(value, current, next)) {
return leftOrRight(value, current, next) ? i : i + 1;
}
}
}
}
}
// define mix max for domain
function quantize(value, domain, range) {
let index = quantizeIndex(value, domain);
return {
range: range[index],
domain: domain[index]
}
}
function quantizeDomain(aDomain, aRange) {
let iMin = aDomain[0];
let iMax = aDomain[1];
let iSlope = (iMax - iMin) / (aRange.length - 1);
let aScale = [];
for (let i = 0; i < aRange.length; i++) {
aScale[i] = (iSlope * i + iMin);
}
return aScale;
}
function generateDomain(sType, aDomain, aRange) {
let aScale = aDomain;
if (sType == 'quantize') {
aScale = quantizeDomain(aDomain, aRange)
}
return aScale;
}
for (let city in oCities) {
// save xy position
oCities[city].x = oCities[city].longitude + 'lon';
oCities[city].y = oCities[city].latitude + 'lat';
chartConfig.shapes.push(drawCity(city));
}
// RENDER CHARTS
// -----------------------------
zingchart.loadModules('maps,maps-bra');
zingchart.render({
id: 'myChart',
data: chartConfig
});