Highcharts: Add a custom menu Copy to clipboard - svg

I wish to add a custom button in highcharts export menu to copy to clipboard with chrome, but it doesn't work and I get the error "Failed to read or decode Blob for clipboard item type image/png".
In my code I am trying to convert an svg image of the chart to png and create a blob then run ClipboardItem on chrome.
https://jsfiddle.net/vmjwr0fo/
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: ''
},
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
series: [{
data: [29.9, 71.5, 106.4, 129.2, 144.0, 176.0, 135.6, 148.5, 216.4, 194.1, 95.6, 54.4]
}],
exporting: {
buttons: {
contextButton: {
menuItems: [
{
text: 'Copy to clipboard',
onclick: async function()
{
const svg = this.getSVG();
const canvas = document.createElement('canvas');
canvas.width = 1200;
canvas.height = 900;
const ctx = canvas.getContext('2d');
const img = document.createElement('img');
img.onload = function()
{
ctx.drawImage(img, 0, 0);
};
img.setAttribute('src', 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))));
const data = new XMLSerializer().serializeToString(img);
const pngImageBlob = await new Blob([data], {type: "image/png"});
try
{
await navigator.clipboard.write([
new ClipboardItem({
'image/png': pngImageBlob
})
]);
}
catch (error)
{
console.error(error.message);
}
}
}
]
}
}
}
});
#container {
height: 400px;
margin: 0 auto;
margin-top: 1em;
min-width: 320px;
max-width: 800px;
}
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<div id="container"></div>
Thank you in advance for your help

Related

getting 0x03 error when performing chart generation: please check your input data in Highchart

i am using highchart export server for generate chart in NodeJs
but when i am generating many charts it gives error like 0x03 error when performing chart generation: please check your input data
here is my code
exports.generateAllCharts = (chartData, callback) => {
highchartsExporter.initPool({
maxWorkers: 100,
initialWorkers: 40,
workLimit: 100,
queueSize: 40,
timeoutThreshold: 600000
});
var allPromises = [];
if(!chartData ||chartData.length === 0) {
return callback({
code: '4',
msg: 'Please send chartdata'
})
}
allPromises.push(exports.getStockChartImg(chartData[1]));
allPromises.push(exports.priceChartVsPeersImg(chartData[2]));
allPromises.push(exports.getPieChartImg(chartData[3]));
allPromises.push(exports.getPieChartImg(chartData[4]));
allPromises.push(exports.getPieChartImg(chartData[5]));
allPromises.push(exports.getPieChartImg(chartData[6]));
allPromises.push(exports.getPieChartImg(chartData[13]));
allPromises.push(exports.getPieChartImg(chartData[14]));
allPromises.push(exports.getPieChartImg(chartData[15]));
allPromises.push(exports.getPieChartImg(chartData[16]));
allPromises.push(exports.getPieChartImg(chartData[18]));
allPromises.push(exports.getPieChartImg(chartData[19]));
allPromises.push(exports.getPieChartImg(chartData[7]));
allPromises.push(exports.getPieChartImg(chartData[17]));
allPromises.push(exports.getPieChartImg(chartData[20]));
allPromises.push(exports.getPieChartImg(chartData[21]));
allPromises.push(exports.getPieChartImg(chartData[22]));
allPromises.push(exports.getPieChartImg(chartData[23]));
allPromises.push(exports.getPieChartImg(chartData[24]));
allPromises.push(exports.getPieChartImg(chartData[25]));
allPromises.push(exports.getPieChartImg(chartData[26]));
allPromises.push(exports.getPieChartImg(chartData[27]));
allPromises.push(exports.getPieChartImg(chartData[33]));
allPromises.push(exports.getPieChartImg(chartData[34]));
allPromises.push(exports.getGlobalOwnershipDistributionChartImg(chartData[35]));
allPromises.push(exports.getPieChartImg(chartData[36]));
allPromises.push(exports.getPieChartImg(chartData[37]));
allPromises.push(exports.getPieChartImg(chartData[38]));
allPromises.push(exports.getPieChartImg(chartData[39]));
allPromises.push(exports.getPieChartImg(chartData[40]));
allPromises.push(exports.getPieChartImg(chartData[41]));
allPromises.push(exports.getPieChartImg(chartData[42]));
allPromises.push(exports.getPieChartImg(chartData[43]));
Promise.all(allPromises)
.then(data => {
highchartsExporter.killPool();
return callback({
code: '0',
custImg: {
pc1: data[0].data,
pc2: data[1].data,
pc3: data[2].data,
pc4: data[3].data,
pc5: data[4].data,
pc6: data[5].data,
pc7: data[6].data,
pc8: data[7].data,
pc9: data[8].data,
pc10: data[9].data,
pc11: data[10].data,
pc12: data[11].data,
pc13: data[12].data,
pc14: data[13].data,
pc17: data[14].data,
pc18: data[15].data,
pc19: data[16].data,
pc20: data[17].data,
pc21: data[18].data,
pc22: data[19].data,
pc23: data[20].data,
pc24: data[21].data,
pc27: data[22].data,
pc28: data[23].data,
pc29: data[24].data,
pc30: data[25].data,
pc31: data[26].data,
pc32: data[27].data,
pc33: data[28].data,
pc34: data[29].data,
pc35: data[30].data,
pc36: data[31].data,
pc37: data[32].data,
}
})
})
.catch(err => callback({
code: '5',
msg: 'Error generating charts',
err,
}))
}
exports.getPieChartImg = (seriesData, xOrLength) => {
var chartOpts = {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
renderTo: 'container',
style: {
fontSize: '20px',
background: '#fffdcc'
},
width:650,
},
credits: {
enabled: false
},
title: {
text: null,
},
tooltip: {
pointFormat: '{series.name}: {point.percentage:.1f}%'
},
legend: {
itemStyle: {
font: 'sans-serif',
fontWeight: 'bold',
fontSize: '13px'
},
useHTML: true,
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
labelFormatter: ()=> {
if (this.name[xOrLength] > 9) {
var words = this.name.split(/[\s]+/);
var numWordsPerLine = 1;
var str = [];
for (var word in words) {
if (parseInt(word) > 0 && parseInt(word) % numWordsPerLine == 0)
str.push('<br>');
str.push(words[word]);
}
var label = str.join(' ');
// Make legend text bold and red if most recent value is less than prior
if (this.name[1] > this.name[2]) {
return '<span style="font-weight:bold">' + label + '</span>';
} else {
return label;
}
} else {
return this.name;
}
}
},
plotOptions: {
pie: {
size: '85%',
allowPointSelect: true,
cursor: 'pointer',
showInLegend: true,
dataLabels: {
enabled: true,
allowOverlap: false,
distance: 10,
formatter: ()=> {
return undefined;
// if (parseFloat(this.percentage.toFixed(2)) > 0.35) {
// return '' + parseFloat(this.percentage).toFixed(2) + '%';
// }
},
padding: 5,
style: { fontFamily: '\'Lato\', sans-serif', /*lineHeight: '18px',*/ fontWeight: 'normal', fontSize: '18px' }
}
},
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: '#6f6f6f',
style: { fontFamily: '\'Lato\', sans-serif', /*lineHeight: '18px',*/ fontWeight: 'normal', fontSize: '18px' },
format:'{point.percentage:.2f}'
},
pointWidth: 30,
cursor: 'pointer'
}
},
series: [{
name: "Value",
type: 'pie',
data: seriesData
}],
navigation: {
buttonOptions: {
enabled: false
}
},
};
var exportSettings = generateExportSettings(chartOpts, 'Stock');
return generateBase64Chart(exportSettings, 3)
}
function generateExportSettings(chartOpts, constr) {
return {
// b64: true,
instr: JSON.stringify(chartOpts),
noDownload: true,
constr,
globalOptions: {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
lang: {
thousandsSep: ','
}
},
scale: 2,
styledMode: false,
type: "image/png",
width: false,
};
}
function generateBase64Chart(exportSettings, number) {
return new Promise((resolve, reject) => {
//Perform an export
highchartsExporter.export(exportSettings, function (err, res) {
//The export result is now in res.
//If the output is not PDF or SVG, it will be base64 encoded (res.data).
//If the output is a PDF or SVG, it will contain a filename (res.filename).
if(err) {
Logger.error("IN ERROR: ", number);
Logger.error("ERROR: ", err);
return reject({
code: '1',
err,
msg: 'Error in stock chart',
exportSettings
})
}
return resolve({
code: '0',
msg: 'success',
data: 'data:image/png;base64,' + res.data,
})
//Kill the pool when we're done with it, and exit the application
// highchartsExporter.killPool();
// process.exit(1);
});
})
}
i am generating all charts at a time, so how can i solve this problem.
I have modified your code a little bit (and used the getPieChartImg as it is the only one available) and tried to export a big number of charts (35 in this case). I don't encounter the 0x03-error. Here is the modified code:
(test.js)
const exporter = require('./promise-based.js');
let numberOfCharts = 35;
let chartsData = [];
for (let i = 0; i < numberOfCharts; i++) {
chartsData.push([1, 2, 3, 4, 5]);
}
exporter.generateAllCharts(chartsData, results => {
if (results.code === '0') {
console.log('All charts exported!');
console.log(results);
} else {
console.log('Error #' + results.code + ': ' + results.msg);
if (results.err) {
console.log(results.err);
}
}
process.exit();
});
(promise-based.js)
const highchartsExporter = require('highcharts-export-server');
let promiseId = 0;
exports.generateAllCharts = (chartData, callback) => {
let allPromises = [];
let chartsLen = chartData.length;
highchartsExporter.logLevel(4);
highchartsExporter.initPool({
maxWorkers: 100,
initialWorkers: 50,
workLimit: 100,
queueSize: 50,
timeoutThreshold: 10000
});
if (!chartData || !chartsLen) {
highchartsExporter.killPool();
return callback({
code: '4',
msg: 'Please send chartdata'
});
}
for (let i = 0; i < chartsLen; i++) {
allPromises.push(
new Promise((resolve, reject) => {
exports.getPieChartImg(chartData[i], false, results => {
if (results.code !== '0') {
return reject(results);
}
return resolve(results);
});
})
);
}
Promise.all(allPromises)
.then(data => {
highchartsExporter.killPool();
let imagesObject = {
code: '0',
custImg: {}
};
data.forEach((image, index) => {
imagesObject.custImg['pc' + (index + 1)] = image.data;
imagesObject.custImg.promiseId = image.promiseId;
});
return callback(imagesObject);
})
.catch(err => callback({
code: '5',
msg: 'Error generating charts',
err
}));
};
exports.getPieChartImg = (seriesData, xOrLength, cb) => {
let chartOpts = {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
chart: {
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false,
renderTo: 'container',
style: {
fontSize: '20px',
background: '#fffdcc'
},
width: 650,
},
credits: {
enabled: false
},
title: {
text: null,
},
tooltip: {
pointFormat: '{series.name}: {point.percentage:.1f}%'
},
legend: {
itemStyle: {
font: 'sans-serif',
fontWeight: 'bold',
fontSize: '13px'
},
useHTML: true,
layout: 'vertical',
align: 'right',
verticalAlign: 'middle',
labelFormatter: () => {
if (this.name[xOrLength] > 9) {
let words = this.name.split(/[\s]+/);
let numWordsPerLine = 1;
let str = [];
for (let word in words) {
if (parseInt(word) > 0 && parseInt(word) % numWordsPerLine == 0)
str.push('<br>');
str.push(words[word]);
}
let label = str.join(' ');
// Make legend text bold and red if most recent value is less than prior
if (this.name[1] > this.name[2]) {
return '<span style="font-weight:bold">' + label + '</span>';
} else {
return label;
}
} else {
return this.name;
}
}
},
plotOptions: {
pie: {
size: '85%',
allowPointSelect: true,
cursor: 'pointer',
showInLegend: true,
dataLabels: {
enabled: true,
allowOverlap: false,
distance: 10,
formatter: () => {
return undefined;
// if (parseFloat(this.percentage.toFixed(2)) > 0.35) {
// return '' + parseFloat(this.percentage).toFixed(2) + '%';
// }
},
padding: 5,
style: {
fontFamily: '\'Lato\', sans-serif',
// lineHeight: '18px',
fontWeight: 'normal',
fontSize: '18px'
}
}
},
series: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: '#6f6f6f',
style: {
fontFamily: '\'Lato\', sans-serif',
// lineHeight: '18px',
fontWeight: 'normal',
fontSize: '18px'
},
format: '{point.percentage:.2f}'
},
pointWidth: 30,
cursor: 'pointer'
}
},
series: [{
name: "Value",
type: 'pie',
data: seriesData
}],
navigation: {
buttonOptions: {
enabled: false
}
},
};
let exportSettings = generateExportSettings(chartOpts, 'Stock');
return generateBase64Chart(exportSettings, 3, cb);
};
function generateExportSettings(chartOpts, constr) {
return {
type: 'png',
constr,
b64: true,
// async: false,
noDownload: true,
scale: 2,
options: chartOpts,
globalOptions: {
colors: ['#7380D4', '#749FD4', '#74BFD4', '#74D4B6', '#99EBA8', '#FEE08B', '#FDAE61', '#F07346', '#E65433', '#C92D22'],
lang: {
thousandsSep: ','
}
}
};
}
function generateBase64Chart(exportSettings, number, cb) {
// Perform an export
highchartsExporter.export(exportSettings, function(err, res) {
// The export result is now in res.
// If the output is not PDF or SVG, it will be base64 encoded (res.data).
// If the output is a PDF or SVG, it will contain a filename (res.filename).
if (err) {
return cb({
code: '1',
msg: 'Error in stock chart',
err,
exportSettings
});
}
promiseId++;
return cb({
code: '0',
msg: 'Success',
promiseId: promiseId,
data: 'data:image/png;base64,' + res.data,
});
// Kill the pool when we're done with it, and exit the application
// highchartsExporter.killPool();
// process.exit(1);
});
}

gsap split Text on slide

I have this part of a slide code, and I want to implement the gsap split Text in h1 (NexText), so that the result is like this: https://codepen.io/GreenSock/pen/aVJRBg
However, I don't know how to implement that in my code.
function nextSlide(pageNumber) {
const nextPage = pages[pageNumber];
const currentPage = pages[current];
const nextLeft = nextPage.querySelector(".hero .model-left");
const nextRight = nextPage.querySelector(".hero .model-right");
const currentLeft = currentPage.querySelector(".hero .model-left");
const currentRight = currentPage.querySelector(".hero .model-right");
const nextText = nextPage.querySelector("h1");
const portofolio = document.querySelector(".portofolio");
const tl = new TimelineMax({
onStart: function() {
slides.forEach(slide => {
slide.style.pointerEvents = "none";
});
},
onComplete: function() {
slides.forEach(slide => {
slide.style.pointerEvents = "all";
});
}
});
tl.fromTo(currentLeft, 0.3, { y: "-10%" }, { y: "-100%" })
.fromTo(currentRight, 0.3, { y: "10%" }, { y: "100%" }, "-=0.2")
.to(portofolio, 0.3, { backgroundImage: backgrounds[pageNumber] })
.fromTo(
currentPage,
0.3,
{ opacity: 1, pointerEvents: "all" },
{ opacity: 0, pointerEvents: "none" }
)
.fromTo(
nextPage,
0.3,
{ opacity: 0, pointerEvents: "none" },
{ opacity: 1, pointerEvents: "all" },
"-=0.6"
)
.fromTo(nextLeft, 0.6, { y: "-100%" }, { y: "-10%" }, "-=0.6")
.fromTo(nextRight, 0.4, { y: "100%" }, { y: "10%" }, "-=0.8")
.fromTo(nextText, 0.3, { opacity: 0, y: 0 }, { opacity: 1, y: 0 })
.set(nextLeft, { clearProps: "all" })
.set(nextRight, { clearProps: "all" });
current = pageNumber;
}
In particular, I don't know how to make these two parts combine:
const nextText = nextPage.querySelector ("h1");
and
var mySplitText = new SplitText ("# quote", {type: "chars, words, lines"})
What you are trying to achieve can be done via:
var variableThatStoresMySplit H1= new SplitText (nextText , {type: "chars"})
And then later in your code you have to use a stagger to through the array SplitText created for you by doing:
gsap.staggerFrom(variableThatStoresMySplit .chars, 0.5, {y:20, opacity:0}, 0.04)
I hope this solves your issue

X-axis multiple colored label for bar chart using chart.js

I need x-axis labels in different colors, I am using "chart.js". I tried below code but it is not working, just showing single color-
scales: {
xAxes: [{
ticks: {
fontColor: [
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(245,88,97,1)',
'rgba(145,151,163,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)',
'rgba(70,180,220,1)'
]
}
}]
}
Output:
Need:
You can make use of the Plugin Core API. It offers different hooks that may be used for executing custom code. In below code snippet, I use the afterDraw hook to draw text of the same color as the corresponding bar.
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
When drawing your own tick labels, you need to instruct Chart.js not to display the default labels. This can be done through the following definition inside the chart options.
scales: {
xAxes: [{
ticks: {
display: false
}
}],
You also need to define some padding for the bottom of the chart, otherwise you won't see your custom tick labels.
layout: {
padding: {
bottom: 20
}
},
Please take a look at the following sample code that illustrates how to change the labels on the x-axis depending on the values.
new Chart('myChart', {
type: 'bar',
plugins: [{
afterDraw: chart => {
var ctx = chart.chart.ctx;
var xAxis = chart.scales['x-axis-0'];
var yAxis = chart.scales['y-axis-0'];
ctx.save();
ctx.textAlign = 'center';
ctx.font = '12px Arial';
chart.data.labels.forEach((l, i) => {
var value = chart.data.datasets[0].data[i];
var x = xAxis.getPixelForValue(l);
ctx.fillStyle = chart.data.datasets[0].backgroundColor[i];
ctx.fillText(l, x, yAxis.bottom + 17);
});
ctx.restore();
}
}],
data: {
labels: ["-3", "-2", "-1", "0", "+1", "+2", "+3"],
datasets: [{
label: "My First Dataset",
data: [60, 59, 80, 81, 60, 55, 40],
fill: false,
backgroundColor: ['rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(245,88,97,1)', 'rgba(145,151,163,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)', 'rgba(70,180,220,1)'],
borderWidth: 1
}]
},
options: {
layout: {
padding: {
bottom: 20
}
},
scales: {
xAxes: [{
ticks: {
display: false
}
}],
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
canvas {
max-width: 300px
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>
<canvas id="myChart" height="200"></canvas>

Highchart y axis need 2 category each having 3 sub category

I am using high chart version v4.0.4, i have worked bar chart like mentioned below i need output with there bar for every year details given below
I am working on below high chart code
var data_first_vd = 0;
var data_second_vd = 0;
var glb_data_ary = [];
var dynamicval1 = 5.85572581;
var dynamicval2 = 0.16091656;
if((dynamicval1>1) || (dynamicval2>1)){
var data_tit = 'Value';
var data_val = '${value}';
var prdelvalaxis = 1000000;
}else{
prdelvalaxis = 1000;
data_tit = "Value";
data_val = "${value}";
}
data_first_vd=5.86;
data_second_vd =0.16;
var data_first_ud =1397.128;
var data_second_ud = 28.145;
data_first_ud_lbl = '1.40M';
data_second_ud_lbl = '28K';
data_first_vd_lbl = '5.86M';
data_second_vd_lbl = '161K';
data_first_vd_lbl_xaxis = '$5.86M';
data_second_vd_lbl_xaxis = '$161K';
var ud1 = 1397;
var ud2 = 28;
var vd1 = 6;
var vd2 = 0;
$('#id').highcharts({
credits: { enabled: false },
chart: {
type: 'bar',
height: 200,
marginLeft: 120,
marginBottom:50,
marginTop: 47,
marginRight:30,
plotBorderWidth: 0,
},
title: {
text: null
},
xAxis: {
drawHorizontalBorders: false,
labels: {
groupedOptions: [{
rotation: 270,
}],
rotation: 0,
align:'center',
x: -30,
y: 5,
formatter: function () {
if (curYear === this.value) {
return '<span style="color:#6C9CCC;">' + this.value + '</span>';
}
else if (prevYear === this.value) {
return '<span style="color: #ED7D31;">' + this.value + '</span>';
}
else if ('VALUE' === this.value) {
return '<span style="color:#942EE1;">' + this.value + '</span>';
}
else{
return '<span style="color: #E124D2;">' + this.value + '</span>';
}
},
useHTML:true
},
categories: [{
name: "UNITS",
categories: [2017, 2016]
},{
name: "VALUE",
categories: [2017, 2016]
}
],
},
yAxis: [{ // Primary yAxis
labels: {
format: data_val,
formatter: function () {
if(this.value!=0){
return '$'+valueConverter_crt(this.value*prdelvalaxis);
}else{
return this.value;
}
},
style: {
color: '#942EE1'
}
},
title: {
text: "<span style='font-size: 12px;'>"+data_tit+"</span>",
style: {
color: '#942EE1'
},
useHTML:true
}
}, { // Secondary yAxis
title: {
text: "<span style='font-size: 12px;'>Units</span>",
style: {
color: '#E124D2'
},
useHTML:true
},
labels: {
format: '{value}',
formatter: function () {
if(this.value!=0){
return cal_pro_del_xaxis(this.value);
}else{
return this.value;
}
},
style: {
color: '#E124D2'
}
},
opposite: true
}],
tooltip: { enabled: false },
exporting: { enabled: false },
legend: { enabled: false },
plotOptions: {
series: {
dataLabels: {
inside: true,
align: 'left',
enabled: true,
formatter: function () {
return this.point.name;
},
color: '#000000',
},
stacking: false,
pointWidth: 15,
groupPadding: 0.5
},
},
series: [{
yAxis: 1,
data: [{y:1397.128,name:data_first_ud_lbl,color:'#6C9CCC'},{y:28.145,name:data_second_ud_lbl,color:'#ED7D31'}],
}, {
data: [null,null,{y:5.86,name:data_first_vd_lbl_xaxis,color:'#6C9CCC'},{y:0.16,name:data_second_vd_lbl_xaxis,color:'#ED7D31'}],
}]
});
I need out put like below chart This chart i draw in paint.
Here 3 bar added in every year
Please help me to achieve this

Google Fusion Tables: Two layers, one with style

I'm trying to create a map using Google Fusion Tables with two layers, one of which I want to add a style to. I also have multiple columns, and I want to switch between these columns using a drop-down menu. So far I managed to do the latter, but I'm stuck trying to add a second layer.
The map I have now shows the ratio of girls:boys in schools in the 170 districts in Ghana. With the drop down menu I can switch between primary school and junior high. Now I want to add a layer with the regional borders.
In the documentation I saw this:
You can use the Maps API to add up to five Fusion Tables layers to a
map, one of which can be styled with up to five styling rules.
Which is pretty much exactly what I want, but I also keep the drop-down menu. I've recently started using Fusion Tables and was hoping someone could help me.
The ID of the layer I want to add: 1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78
And the code of the working map:
PS: I'm a total newbie and used this map made by the Guardian as a basis. I got rid of everything I didn't think I needed, but there might be some unnecessary stuff left in the code.
<!DOCTYPE html>
<html lang="en" itemscope itemtype="http://schema.org/Article">
<head>
<title>Gender Parity Index | Education | 2011</title>
<meta charset="utf-8" />
<style type="text/css">
body { font-family: Arial, sans-serif; }
#map_canvas { height: 600px; width:575px; }
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" id="script">
var center = new google.maps.LatLng(7.972198, -0.716284);
var zoom = 7;
var legend_width = '150px';
var tableid = '12GLQaH4wvwByxBk4W7UHkJTr99vsxymCTYHmkXs';
var location_column = 'geometry';
var colors = ['#CA0020','#F4A582','#F7F7F7','#92C5DE','#0571B0'];
var columns = {
'Gender parity index at primary education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
}
],
'Gender parity index at junior high school education': [
{
'min': 0.6,
'max': 0.8,
'color': '#CA0020'
},
{
'min': 0.8,
'max': 0.95,
'color': '#F4A582'
},
{
'min': 0.95,
'max': 1.05,
'color': '#F7F7F7'
},
{
'min': 1.05,
'max': 1.2,
'color': '#92C5DE'
},
{
'min': 1.2,
'max': 1.6,
'color': '#0571B0'
}
]
}
var map, layer, geocoder;
function initialize() {
map = new google.maps.Map(document.getElementById('map_canvas'), {
center: center,
zoom: zoom,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var style = [ { stylers: [ { invert_lightness: true } ] },{ featureType: "road.highway", stylers: [ { hue: "#00e5ff" } ] },{ featureType: "poi.park", stylers: [ { visibility: "off" } ] },{ featureType: "landscape.natural", stylers: [ { visibility: "on" } ] },{ featureType: "water", stylers: [ { color: "#080808" } ] },{ featureType: "landscape.natural", stylers: [ { color: "#202020" } ] },{ featureType: "administrative.province", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.locality", elementType: "labels", stylers: [ { visibility: "on" } ] },{ featureType: "administrative.country", elementType: "labels", stylers: [ { visibility: "off" } ] },{
featureType: 'road',
elementType: 'all',
stylers: [
{ saturation: -99 }
]
} ];
geocoder = new google.maps.Geocoder();
var styledMapType = new google.maps.StyledMapType(style, {
map: map,
name: 'Styled Map'
});
map.mapTypes.set('map-style', styledMapType);
map.setMapTypeId('map-style');
layer = new google.maps.FusionTablesLayer({
query: {
select: location_column,
from: tableid
}
});
layer.setMap(map);
init_selectmenu();
addStyle(getKey());
}
function getKey() {
for(key in columns) {
return key;
}
}
// Initialize the drop-down menu
function init_selectmenu() {
var selectmenu = document.getElementById('selector');
for(column in columns) {
var option = document.createElement('option');
option.setAttribute('value', column);
option.innerHTML = column;
selectmenu.appendChild(option);
}
}
function addStyle(column) {
var defined_styles = columns[column];
var styles = new Array();
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
styles.push({
where: generateWhere(column, style.min, style.max),
polygonOptions: {
fillColor: style.color,
fillOpacity: 0.9,
strokeOpacity: 0.50,
strokeColor: "#f3f3f3"
}
});
}
layer.set('styles', styles);
updateLegend(column);
}
// Create the where clause
function generateWhere(column_name, low, high) {
var whereClause = new Array();
whereClause.push("'");
whereClause.push(column_name);
whereClause.push("' >= ");
whereClause.push(low);
whereClause.push(" AND '");
whereClause.push(column_name);
whereClause.push("' < ");
whereClause.push(high);
return whereClause.join('');
}
// Create the legend with the corresponding colors
function updateLegend(column) {
var legendDiv = document.createElement('div');
var legend = new Legend(legendDiv, column);
legendDiv.index = 1;
map.controls[google.maps.ControlPosition.RIGHT_TOP].pop();
map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legendDiv);
}
// Generate the content for the legend
function Legend(controlDiv, column) {
controlDiv.style.padding = '10px';
var controlUI = document.createElement('div');
controlUI.style.backgroundColor = 'white';
controlUI.style.borderStyle = 'solid';
controlUI.style.borderWidth = '1px';
controlUI.style.width = legend_width;
controlUI.title = 'Legend';
controlDiv.appendChild(controlUI);
var controlText = document.createElement('div');
controlText.style.fontFamily = 'Arial,sans-serif';
controlText.style.fontSize = '12px';
controlText.style.paddingLeft = '4px';
controlText.style.paddingRight = '4px';
controlText.innerHTML = legendContent(column);
controlUI.appendChild(controlText);
}
function legendContent(column) {
var defined_styles = columns[column];
// Generate the content for the legend using colors from object
var controlTextList = new Array();
controlTextList.push('<p><b>');
controlTextList.push(column);
controlTextList.push('</b></p>');
for(defined_style in defined_styles) {
var style = defined_styles[defined_style];
controlTextList.push('<div style="background-color: ');
controlTextList.push(style.color);
controlTextList.push('; height: 20px; width: 20px; margin: 3px; float: left;"></div>');
controlTextList.push(style.min);
controlTextList.push(' to ');
controlTextList.push(style.max);
controlTextList.push('<br style="clear: both;"/>');
}
controlTextList.push('<br />');
return controlTextList.join('');
}
</script>
</head>
<body onload="initialize();">
<select onchange="addStyle(this.value);" id="selector" style="font-size: 16px"></select>
<div id="map_canvas"></div>
</div>
<script>
// send the query string to the iFrame
(function() {
var interactive = jQ('#interactive iframe');
if (interactive.length > 0) {
var qs = window.location.search;
interactive[0].src = interactive[0].src + qs;
}
})();
</script>
</div>
<div id="related">
</script>
</script>
</body>
</html>
Just add the second layer at the end of your initialize() method like that:
function initialize() {
// ... all the other stuff ...
borderLayer = new google.maps.FusionTablesLayer({
query: {
select: 'geometry',
from: '1_0rcifQnnNpLV1VjTPyzDZSF3LHp-7rowzrXM78'
}
});
borderLayer.setMap(map);
}
See the working example of your code with the second layer on jsFiddle.

Resources