I'm building an election heat map using Google Fusion Tables, the Maps API and the FusionTablesLayer. For a given candidate, the map will shade counties darker depending on the percentage of the vote they won.
I'm using layer.setOptions() to set up buckets of percentages and setting progressively darker fillColors based on where the vote fell. For example, for Santorum:
layer.setOptions({
query:
{
select: 'geometry',
from: '3102804'
},
styles:
[{
polygonOptions:
{
fillColor:"#000000",
fillOpacity: .8
}
},
{
where: "SantorumPercentage < '.04'",
polygonOptions:
{
fillColor:"#ffeaeb"
}
},
{
where: "SantorumPercentage < '.08' AND SantorumPercentage >= '.04'",
polygonOptions:
{
fillColor:"#fedada"
}
},
{
where: "SantorumPercentage < '.14' AND SantorumPercentage >='.08' ",
polygonOptions:
{
fillColor:"#fec9ca"
}
},
{
where: "SantorumPercentage < '.18' AND SantorumPercentage >= '.14' ",
polygonOptions:
{
fillColor:"#feb8ba"
}
},
{
where: "SantorumPercentage < '.22' AND SantorumPercentage >= '.18' ",
polygonOptions:
{
fillColor:"#fda8aa"
}
},
{
where: "SantorumPercentage < '.26' AND SantorumPercentage >='.22' ",
polygonOptions:
{
fillColor:"#fd9799"
}
},
{
where: "SantorumPercentage < '.30' AND SantorumPercentage >= '.26' ",
polygonOptions:
{
fillColor:"#fd8689"
}
},
{
where: "SantorumPercentage < '.34' AND SantorumPercentage >= '.30' ",
polygonOptions:
{
fillColor:"#fc7679"
}
},
{
where: "SantorumPercentage < '.38' AND SantorumPercentage >= '.34' ",
polygonOptions:
{
fillColor:"#fc6569"
}
},
{
where: "SantorumPercentage < '.42' AND SantorumPercentage >= '.38' ",
polygonOptions:
{
fillColor:"#fc5459"
}
},
{
where: "SantorumPercentage < '.46' AND SantorumPercentage >= '.42' ",
polygonOptions:
{
fillColor:"#fb4448"
}
},
{
where: "SantorumPercentage < '.50' AND SantorumPercentage >= '.46' ",
polygonOptions:
{
fillColor:"#fb3338"
}
},
{
where: "SantorumPercentage < '.54' AND SantorumPercentage >= '.50' ",
polygonOptions:
{
fillColor:"#fb2228"
}
},
{
where: "SantorumPercentage < '.60' AND SantorumPercentage >= '.54' ",
polygonOptions:
{
fillColor:"#fb2228"
}
},
{
where: "SantorumPercentage >= '.60'",
polygonOptions:
{
fillColor:"#f6050b"
}
}]
});
However, FT only appears to recognize the first 4 "buckets," rendering counties where Santorum won more than 18 percent of the vote as the default shade, i.e. black.
Is this a problem with my code? Or does FT only allow up to four conditional styles?
You should be able to apply 5 styles to a single layer, per the Google Maps API documentation:
http://code.google.com/apis/maps/documentation/javascript/layers.html#fusion_table_styles
I've ran into this same issue before on another project, and was unable to to solve it via the Maps API. To workaround I used the FT API Styling which can be cumbersome when doing the type of styling you are doing. I also found a post reply from Rebecca with FT that explains you "can apply up to 5 styling rules." I have not been able to find this yet in the fusion tables documentation.
Another possible workaround could be to do something similar as the custom icon workaround, request the geometry and then display it as a polygon overlay.
https://developers.google.com/fusiontables/docs/samples/custom_markers
Related
I have an API which returns JSON data like this:
[
{
"name":"Something",
"data":[
{
"x":1541096421,
"y":2
},{
"x":1541436378,
"y":4
},{
"x":1553621371,
"y":2
}
]
},{
"name":"Something else",
"data":[
{
"x":1541096421,
"y":2
},{
"x":1541436378,
"y":4
},{
"x":1553621371,
"y":2
}
]
}
]
The x axis represents date/time and the y axis is a score. It's plotted on a chart like this, using some formatting to convert the date from millisecond timestamp to a readable date format:
function renderChart(data) {
$('#chartContainer').highcharts({
chart: {
type: 'scatter',
zoomType: 'xy'
},
title: {
text: chartTitle()
},
xAxis: {
allowDecimals: false,
title: {
text: 'Date completed',
scalable: false
},
type: 'datetime',
labels: {
formatter: function () {
if (true) {
return Highcharts.dateFormat('%d-%b-%y', moment.unix(this.value));
}
else {
if (this.value > 0 && this.value < 24) {
return this.value;
}
else
return 0;
}
}
},
tickPixelInterval: 100
},
yAxis: {
title: {
text: 'Score'
}
},
plotOptions: {
scatter: {
marker: {
radius: 5
}
}
},
series: data,
exporting: {
buttons: {
contextButton: {
menuItems: Highcharts.getOptions().exporting.buttons.contextButton.menuItems.filter(item => item !== 'openInCloud')
}
}
// Tried adding this but it doesn't make any difference:
/*,
csv: {
dateFormat: '%d/%m/%Y'
}*/
},
tooltip: {
formatter: function () {
return 'Score of <b>' + this.y + '</b> posted on <b>' + Highcharts.dateFormat('%d-%b-%y', moment.unix(this.x)) + '</b>';
}
}
});
}
This works fine. However, when I click 'export to CSV' in the Highchart graph on the front-end it outputs a CSV file where the date is always showing as "18/01/1970". Obviously it's something to do with the fact that the API is returning a timestamp value, but I don't see how I can modify the format in the CSV similar to how it's done in the chart rendering code.
Can anyone advise how (preferably without modifying the data returned by the API) to get the CSV to output a correct date in day/month/year format?
Many thanks
It can be done easily by wrapping Highcharts.Chart.prototype.getDataRows method and map the data array which is used for export. Check demo and code posted below.
Code:
(function(H) {
H.wrap(H.Chart.prototype, 'getDataRows', function(proceed, multiLevelHeaders) {
var rows = proceed.call(this, multiLevelHeaders);
rows = rows.map(row => {
if (row.x) {
row[0] = Highcharts.dateFormat('%d-%b-%y', row.x * 1000);
}
return row;
});
return rows;
});
}(Highcharts));
Demo:
https://jsfiddle.net/BlackLabel/yafx8cb1/1/
Docs:
https://www.highcharts.com/docs/extending-highcharts/extending-highcharts
As per #Core972's comment, the issue here is related to the timestamp in the API returning the date as seconds rather than milliseconds. I don't believe there is a way to manipulate the date format in the CSV export specifically so it will require a change to the API which returns the data.
Wojciech Chmiel's answer demonstrates how to override Highchart's output to re-format the date from a non-ideal source.
I've implemented a polar chart in which each series has 4 values corresponding to 4 categories. When I export the chart csv, the category column contains polar coordinates. I would like to replace these with the corresponding category name. How do I do this?
Adding the categories to each series, had no effect. I also tried adding a categories property to the xAxis, but it had not effect. An xAxis.label formatter successfully returns the category name for each data polar coordinate.
const options = {
chart: {
polar: true,
},
title: {
text: '',
},
tooltip: {
valueDecimals: 2,
headerFormat: '<br/>',
},
legend: {},
pane: {
startAngle: 0,
endAngle: 360,
},
xAxis: {
tickInterval: 45,
min: 0,
max: 360,
labels: {
// eslint-disable-next-line
formatter: function() {
switch (this.value) {
case 45:
return '<b>Experience</b>'
case 135:
return '<b>Frictionless</b>'
case 225:
return '<b>Low Price</b>'
case 315:
return '<b>Brand</b>'
default:
return ''
}
},
},
},
yAxis: {
min: 0,
max: 10,
labels: {
format: '{}',
},
},
plotOptions: {
series: {
pointStart: 45,
pointInterval: 90,
},
column: {
pointPadding: 0,
groupPadding: 0,
},
},
series: kahnSeries,
}
You need to use categories property, but without options like: pointInterval, pointStart, min and max:
xAxis: {
categories: ['Experience', 'Frictionless', 'Low Price', 'Brand']
},
Live demo: http://jsfiddle.net/BlackLabel/z8cm1p39/
API Reference: https://api.highcharts.com/highcharts/xAxis.categories
To avoid changing the chart's current display, I wrapped the getCSV function and replaced the CSV category values. If there was a simpler way, please share it.
{
(function (H) {
H.wrap(H.Chart.prototype, 'getCSV', function (
proceed,
useLocalDecimalPoint
) {
// Run the original proceed method
const result = proceed.apply(
this,
Array.prototype.slice.call(arguments, 1)
)
const itemDelimiter = ','
const lineDelimiter = '\n'
const rows = result.split(lineDelimiter)
let newResult = ''
let rowCategories = false
rows.forEach((row, rowIndex) => {
const columns = row.split(itemDelimiter)
if (rowIndex === 0 && columns[0] === '"Category"') {
rowCategories = true
}
if (rowIndex > 0 && rowCategories) {
let newRow = formatter(columns[0])
columns.forEach((column, columnIndex) => {
if (columnIndex > 0) {
newRow += itemDelimiter
newRow += column
}
})
newResult += newRow
} else {
newResult += row
}
if (rowIndex < rows.length - 1) {
newResult += lineDelimiter
}
}
)
return newResult
})
}(Highcharts))
}
Hi everyone I am new using highcharts, I have my data structure and when I try to show, I don't see anything
function nueva (current_data){
var seriesOptions = [],
seriesCounter = 0,
type = ['jobs_running', 'jobs_pending'];
function createChart() {
$('#containerChart').highcharts('StockChart', {
rangeSelector: {
selected: 4
},
tooltip: {
pointFormat: '<span style="color:{series.color}">{series.name}</span>: <b>{point.y}</b> ({point.change}%)<br/>',
valueDecimals: 2
},
series: seriesOptions
});
}
for (var j = 0; j < current_data['names'].length; j++){
var all_element = []
name_project = current_data['names'][j];
for (var i = 0; i < type.length; i++){
seriesCounter = 0;
for (var i = 0; i < type.length; i++){
seriesOptions[j] = {
name: type[i],
data: current_data[name_project][type[i]],
};
}
}
createChart();
}
}
I pass current_data to my function that is like this
I want to show 'jobs_running' and 'jobs_pendding' I set the value to seriesOptions
and my array of data has this
Any idea why I don't see anything in the chart! I am missing something.
Thanks in advance
Hopefully you can find your answer in this:
https://jsfiddle.net/ekekp8rh/1/
Not sure what you were hoping to get but at least this displays a chart.
var data = {
projects: [{
name: 'Project X',
jobs_running: [
[1459814400000, 121],
[1459900800000, 205],
[1459987200000, 155],
[1460073600000, 458]
],
jobs_pending: [
[1459814400000, 146],
[1459900800000, 149],
[1459987200000, 158],
[1460073600000, 184]
]
}, {
name: 'Project Y',
jobs_running: [
[1459814400000, 221],
[1459900800000, 295],
[1459987200000, 255],
[1460073600000, 258]
],
jobs_pending: [
[1459814400000, 246],
[1459900800000, 249],
[1459987200000, 258],
[1460073600000, 284]
]
}]
};
nueva(data);
function nueva(current_data) {
var seriesOptions = [],
type = ['jobs_running', 'jobs_pending'];
for (var j = 0; j < current_data['projects'].length; j++) {
var project = current_data['projects'][j];
for (var i = 0; i < type.length; i++) {
seriesOptions.push({
name: project.name + ' ' + type[i],
data: project[type[i]]
});
}
}
$('#containerChart').highcharts('StockChart', {
series: seriesOptions
});
}
I have Highcharts Gauge chart with multiple needles and the datalabels of the needles are overlapped each other like http://jsfiddle.net/edLHB/3/
plotOptions I have used as following
plotOptions: {
guage: {
dataLabels: {
enabled: true,
crop: false,
overflow: 'none'
}
}
},
Please help me to get datalabels without overlapping.
Thanks
Give them individual x or y values in the series
series: [{
name: 'Speed',
data: [80],
dataLabels: {
y:50
}
}, {
name: 'Speed',
data: [100],
dataLabels: {
y:30
}
}, {
name: 'Speed',
data: [150],
dataLabels: {
y:10
}
}]
Demo: http://jsfiddle.net/robschmuecker/edLHB/7/
In general this solution is not supported (I mean collision detection), but you adapt this solution http://jsfiddle.net/menXU/6/.
function StaggerDataLabels(series) {
sc = series.length;
if (sc < 2) return;
for (s = 1; s < sc; s++) {
var s1 = series[s - 1].points,
s2 = series[s].points,
l = s1.length,
diff, h;
for (i = 0; i < l; i++) {
if (s1[i].dataLabel && s2[i].dataLabel) {
diff = s1[i].dataLabel.y - s2[i].dataLabel.y;
h = s1[i].dataLabel.height + 2;
if (isLabelOnLabel(s1[i].dataLabel, s2[i].dataLabel)) {
if (diff < 0) s1[i].dataLabel.translate(s1[i].dataLabel.translateX, s1[i].dataLabel.translateY - (h + diff));
else s2[i].dataLabel.translate(s2[i].dataLabel.translateX, s2[i].dataLabel.translateY - (h - diff));
}
}
}
}
}
I'm trying to apply multiple styles to a Google Fusion Table Layer.
This works and colors all polygons that are in an array to the blue color:
layer = new google.maps.FusionTablesLayer({
map : map,
query : {
select : "geometry",
from : "1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA"
},
styles: [{
polygonOptions:
{
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
},
{
where: whereClause,
polygonOptions: {
fillColor: "#0D58A6"
}
}
]
});
layer.setMap(map);
But this doesn't work -- no polygons even appear on my map:
layer = new google.maps.FusionTablesLayer({
map : map,
query : {
select : "geometry",
from : "1gwSN6n_00uZ7YuAP7g4FiUiilybqDRlRmWJrpvA"
},
styles: [{
polygonOptions:
{
fillColor: "#ffffff",
strokeColor: "#bcbcbc",
fillOpacity: ".75"
}
},
{
where: whereClause,
polygonOptions: {
fillColor: "#0D58A6"
}
}
,
{
where: whereClause,
polygonOptions: {
fillColor: "#ff0000"
}
}
]
});
layer.setMap(map);
Never mind that I'm coloring the same thing one color and then another--I just want the second style to work. When I take out the second style, all is fine. I put in the second style--and no polygons any more.
Can someone tell me what I'm doing wrong, please?
This may very well be an error (the exact same where clause with two different styles):
{
where: whereClause,
polygonOptions: {
fillColor: "#0D58A6"
}
}
,
{
where: whereClause,
polygonOptions: {
fillColor: "#ff0000"
}
}
I would expect the where clause to have to be unique.
UPDATE:
My current guess is that you are running into a query limit on the size of the data sent back to the server.
Each of the whereClauses works independently
If this is the issue (the query string is too big), mapping the "COUSUBFP" codes to something shorter (3 decimal digits or two hex digits), might make it work (or for that matter, just truncating the leading "0's").
03320 -> 0
03608 -> 1
etc.