Is it possible to define the cell color with a nested property of the object mapped to the table ?
The JSON structure of the objects is :
objects: [
{
"agent": "agent_1",
"days": {
day_0: {
"code": "ABC",
"color": "#0062cc"
},
day_1: {
"code": "DEF",
"color": "#a09494b2"
}
},
{
[...]
}
]
I have a table defined like this :
let columns = [
{title: "Agent", dataKey: "agent"},
{title: "january 1st", dataKey: "day_0"},
{title: "january 2nd", dataKey: "day_1"}]
let rows = [
{agent: "agent_1", day_0: "ABC", day_1: "DEF"},
[...]
]
All that works fine. But I'd like to set the color of each day cell dynamically, set with the color code of the corresponding object. Something like :
createdCell: function(cell, data) {
{
cell.styles.fillColor = "day_0.color";
}
}
But I can't figure how to pass the data to the table. Is it possible ? Can displayProperty help in any way ?
EDIT: In this case it was that v2.3.4 of jspdf-autotable was needed
Based on our comments discussion I think I understood your problem. You can try something like this (with the hexToRgb function from here)
let columns = [{
title: "Agent",
dataKey: "agent"
},
{
title: "january 1st",
dataKey: "day_0"
},
{
title: "january 2nd",
dataKey: "day_1"
}
]
let objects = [{
agent: "agent_1",
day_0: {
"code": "ABC",
"color": "#00ff00"
},
day_1: {
"code": "DEF",
"color": "#ff0000"
}
// etc
}];
let doc = jsPDF()
doc.autoTable(columns, objects, {
createdCell: function(cell, data) {
let hex = cell.raw.color
if (hex) {
let rgb = hexToRgb(hex)
cell.styles.fillColor = rgb;
cell.text = cell.raw.code
}
}
});
doc.save('jhg.pdf')
function hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.4.1/jspdf.debug.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/2.3.4/jspdf.plugin.autotable.js"></script>
I just leave the answer here just in case anyone needs it.
We can use the didParseCell hook.
doc.autoTable({
head: [[..., color]],
body: [[..., #ffffff], [..., #ff0000]], // pass hexa value to the cell
didParseCell: function (HookData) {
if (HookData.cell == undefined)
return;
// find cell taht contains the hexa value
// the change the fillColor property
// and set the cell value to empty
var color = HookData.cell.text[0];
if (color.match(/^#[a-fA-F0-9]{3}([a-fA-F0-9]{3})/g) != null) {
HookData.cell.styles.fillColor = hexToRgb(color);
HookData.cell.text = [];
}
}
});
Code to convert hexa to RGB:
hexToRgb(hex) {
var bigint = parseInt(hex.replace('#', ''), 16);
var r = (bigint >> 16) & 255;
var g = (bigint >> 8) & 255;
var b = bigint & 255;
return [r, g, b];
}
Package version:
jspdf : 2.5.1
jspdf-autotable :3.5.25
Related
I am using this script to draw average spectral signature of all classes together and each class separately of a classified image by RF algorithm in GEE.
var bands = ['B1', 'B2', 'B3', 'B4','B5','B6','B7', 'B8', 'B8A', 'B9' ,'B11', 'B12','NDVI', 'EVI', 'GNDVI', 'NBR', 'NDII'];
var Training_Points = Water.merge(Residential).merge(Agricultural).merge(Arbusti).merge(BoschiMisti).merge(Latifoglie).merge(Conifere).merge(BareSoil);
var classes = ee.Image().byte().paint(Training_Points, "land_class").rename("land_class")
var stratified_points = classes.stratifiedSample({
numPoints: 50,
classBand: 'land_class',
scale: 10,
region: Training_Points,
geometries: false,
tileScale: 6
})
print(stratified_points, 'stratified_points')
//Create training data
var training_Stratified = RF_classified.select(bands).sampleRegions({
collection: stratified_points,
properties: ['land_class'],
scale:10,
tileScale:2
});
var bands = RF_classified.bandNames()
var numBands = bands.length()
var bandsWithClass = bands.add('land_class')
var classIndex = bandsWithClass.indexOf('land_class')
// Use .combine() to get a reducer capable of computing multiple stats on the input
var combinedReducer = ee.Reducer.mean().combine({
reducer2: ee.Reducer.stdDev(),
sharedInputs: true})
// Use .repeat() to get a reducer for each band and then use .group() to get stats by class
var repeatedReducer = combinedReducer.repeat(numBands).group(classIndex)
var stratified_points_Stats = training_Stratified.reduceColumns({
selectors: bands.add('land_class'),
reducer: repeatedReducer,
})
// Result is a dictionary, we do some post-processing to extract the results
var groups = ee.List(stratified_points_Stats.get('groups'))
var classNames = ee.List(['Water','Residential', 'Agricultural', 'Arbusti', 'BoschiMisti', 'Latifoglie','Conifere', 'BareSoil'])
var fc = ee.FeatureCollection(groups.map(function(item) {
// Extract the means
var values = ee.Dictionary(item).get('mean')
var groupNumber = ee.Dictionary(item).get('group')
var properties = ee.Dictionary.fromLists(bands, values)
var withClass = properties.set('class', classNames.get(groupNumber))
return ee.Feature(null, withClass)
}))
// Chart spectral signatures of training data
var options = {
title: 'Average Spectral Signatures',
hAxis: {title: 'Bands'},
vAxis: {title: 'Reflectance',
viewWindowMode:'explicit',
viewWindow: {
max:6000,
min:0
}},
lineWidth: 1,
pointSize: 4,
series: {
0: {color: '105af0'},
1: {color: 'dc350a'},
2: {color: 'caa712'},
3: {color: 'b9ffa4'},
4: {color: '369b47'},
5: {color: '21ff2d'},
6: {color: '275b25'},
7: {color: 'f7e084'},
}};
// Default band names don't sort propertly Instead, we can give a dictionary with labels for each band in the X-Axis
var bandDescriptions = {
'B2': 'B2/Blue',
'B3': 'B3/Green',
'B4': 'B4/Red',
'B5': 'B5/Red Edge 1',
'B6': 'B5/Red Edge 2',
'B7': 'B7/Red Edge 3',
'B8': 'B8/NIR',
'B8A': 'B8A/Red Edge 4',
'B11': 'B11/SWIR-1',
'B12': 'B12/SWIR-2'
}
// Create the chart and set options.
var chart = ui.Chart.feature.byProperty({
features: fc,
xProperties: bandDescriptions,
seriesProperty: 'class'
})
.setChartType('ScatterChart')
.setOptions(options);
print(chart)
var classChart = function(land_class, label, color) {
var options = {
title: 'Spectral Signatures for ' + label + ' Class',
hAxis: {title: 'Bands'},
vAxis: {title: 'Reflectance',
viewWindowMode:'explicit',
viewWindow: {
max:6000,
min:0
}},
lineWidth: 1,
pointSize: 4,
};
var fc = training_Stratified.filter(ee.Filter.eq('land_class', land_class))
var chart = ui.Chart.feature.byProperty({
features: fc,
xProperties: bandDescriptions,
})
.setChartType('ScatterChart')
.setOptions(options);
print(chart)
}
classChart(0, 'Water')
classChart(1, 'Residential')
classChart(2, 'Agricultural')
classChart(3, 'Arbusti')
classChart(4, 'BoschiMisti')
classChart(5, 'Latifoglie')
classChart(6, 'Conifere')
classChart(7, 'BareSoil')
I receive the error:
Error generating chart: Image.select: Pattern 'B1' did not match any
bands.
I do not understand where is the problem since I used the same script before to draw histogram of training data and it worked well.
Is there any way to convert this=> google sheet into the below given JSON object using AppScript. I'm new to AppScript I dont have idea how to make this row and column in the below given object. Thanks in advance.
{
"data":[
{
"insurer":"CompanyName1",
"products":[
{
"name":"product1",
"UIN":"104N079V01"
},
{
"name":"product2",
"UIN":"104N079V02"
}
]
},
{
"insurer":"CompanyName2",
"products":[
{
"name":"product1",
"UIN":"104N079V01"
},
{
"name":"product2",
"UIN":"104N079V02"
}
]
}
]
}
Try
function data2json() {
var sh = SpreadsheetApp.getActiveSheet()
var values = sh.getRange('A1').getDataRegion().getValues()
// Logger.log(values)
var jsn = {}
jsn['data'] = []
var n = -1
var m = 0
values.forEach(function (r, i) {
if (i > 0) {
if (r[1] != '') {
n++
jsn['data'][n] = {}
jsn['data'][n]['insurer'] = r[1]
jsn['data'][n]['products'] = []
m = -1
}
m++
jsn['data'][n]['products'][m] = {}
jsn['data'][n]['products'][m]['name'] = r[2]
jsn['data'][n]['products'][m]['UIN'] = r[3]
}
})
Logger.log(JSON.stringify(jsn))
}
example
result
{"data":[{"insurer":"CompanyName1","products":[{"name":"product1","UIN":"A"},{"name":"product2","UIN":"B"},{"name":"product3","UIN":"C"},{"name":"product4","UIN":"D"}]},{"insurer":"CompanyName2","products":[{"name":"product5","UIN":"E"},{"name":"product6","UIN":"F"},{"name":"product7","UIN":"G"},{"name":"product8","UIN":"H"}]},{"insurer":"CompanyName3","products":[{"name":"product9","UIN":"I"},{"name":"product10","UIN":"J"},{"name":"product11","UIN":"K"}]}]}
I'm making a sunburst with Highcharts .NET,
This is how i setup the chart:
Highcharts higcharts = new Highcharts
{
Chart = new Chart
{
Type = ChartType.Sunburst,
Width = 700,
Height = 700
},
Title = new Title
{
Text = "Monthly Average Temperature",
X = -20
},
Subtitle = new Subtitle
{
Text = "Source: WorldClimate.com",
X = -20
},
Legend = new Legend
{
Layout = LegendLayout.Vertical,
Align = LegendAlign.Right,
VerticalAlign = LegendVerticalAlign.Middle,
BorderWidth = 0
},
Series = new List<Series>
{
new SunburstSeries
{
Name ="Test",
Data = data,
//LevelSize = new SunburstSeriesLevelSize
//{
// Unit = SunburstSeriesLevelSizeUnit.Percentage,
// Value = 100
//},
Levels = new List<SunburstSeriesLevels>
{
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 90
}
},
new SunburstSeriesLevels
{
LevelSize = new SunburstSeriesLevelsLevelSize{
Unit = SunburstSeriesLevelsLevelSizeUnit.Percentage,
Value = 10
}
}
}
}
}
};
I tried many ways but the levels radius never change, did i miss something?
The only one working is the levelsize of the entire serie but i need to set the size for a specific level.
I tried to search but it looks like nobody already encountered any problem.
Level's object levelSize is able do control the size of individual level. It has two properties: unit (pixels / percentage / weight) and value (determined by the unit):
levels: [{
level: 1,
levelIsConstant: false,
levelSize: {
unit: 'pixels',
value: 30
}
}, {
level: 2,
colorByPoint: true,
dataLabels: {
rotationMode: 'parallel'
}
}, {
level: 3,
levelIsConstant: true,
levelSize: {
unit: 'weight',
value: 2
}
}, {
level: 4,
levelIsConstant: true,
levelSize: {
unit: 'percentage',
value: 30
}
}]
Live demo: http://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/plotoptions/sunburst-levelsize/
API reference: https://api.highcharts.com/highcharts/series.sunburst.levelSize
I build a chart like the lazy loading example (http://www.highcharts.com/stock/demo/lazy-loading) but with having the input fields of the range selector.
When I zoom in everything is fine. Now I would like to change the selected range by using the input fields.
But I am not able to change the input to values beyond the zoomed area despite the fact I have more data visible in my navigator.
In the setExtremes function I am doing some calculations:
DX.IPFixGraphModule.prototype.setExtremes = function(e) {
var fromTime,
maxZoom = 30 * 60 * 1000,
now = new Date().getTime();
if (e.min) {
fromTime = e.min;
} else {
fromTime = e.dataMin;
}
fromTime = Math.round(fromTime);
var diff = now - fromTime;
// -1 month = max 1 week zoom
if (diff >= 2592000000) {
maxZoom = 7 * 24 * 60 * 60 * 1000;
}
// -1 week = max 12 hour zoom
else if (diff >= 604800000) {
maxZoom = 12 * 60 * 60 * 1000;
}
// this refers to axis
// #see http://api.highcharts.com/highstock#Axis.update
this.update({
minRange: maxZoom
}, false);
};
But the values I receive in e.min and e.max are not the original input values but already corrected to the displayed time range.
// handle changes in the input boxes
input.onchange = function () {
var inputValue = input.value,
value = (options.inputDateParser || Date.parse)(inputValue),
xAxis = chart.xAxis[0],
dataMin = xAxis.dataMin,
dataMax = xAxis.dataMax;
// If the value isn't parsed directly to a value by the browser's Date.parse method,
// like YYYY-MM-DD in IE, try parsing it a different way
if (isNaN(value)) {
value = inputValue.split('-');
value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
}
if (!isNaN(value)) {
// Correct for timezone offset (#433)
if (!defaultOptions.global.useUTC) {
value = value + new Date().getTimezoneOffset() * 60 * 1000;
}
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
if (isMin) {
if (value > rangeSelector.maxInput.HCTime) {
value = UNDEFINED;
} else if (value < dataMin) {
value = dataMin;
}
} else {
if (value < rangeSelector.minInput.HCTime) {
value = UNDEFINED;
} else if (value > dataMax) {
value = dataMax;
}
}
// Set the extremes
if (value !== UNDEFINED) {
chart.xAxis[0].setExtremes(
isMin ? value : xAxis.min,
isMin ? xAxis.max : value,
UNDEFINED,
UNDEFINED,
{ trigger: 'rangeSelectorInput' }
);
}
}
};
(Code taken from highstock.src.js around line 21126)
So I cannot extend my zoom beyond the current active selection, but the navigator displays more data.
Does anyone know a way to set a date beyond the currently zoomed time range?
Possible Solution
I solved it by checking the navigator range in the "afterSetExtremes" Event.
DX.IPFixGraphModule.prototype.afterSetExtremes = function(e) {
if (e.trigger === 'rangeSelectorInput') {
var fromValue = this.stockchart.rangeSelector.minInput.value,
toValue = this.stockchart.rangeSelector.maxInput.value,
fromTime = parseDateTime(fromValue),
toTime = parseDateTime(toValue),
navigatorAxis = this.stockchart.get('navigator-x-axis'),
maxValue = navigatorAxis.dataMax,
minValue = navigatorAxis.dataMin;
if (fromTime < minValue) {
fromTime = minValue;
}
if (toTime > maxValue) {
toTime = maxValue;
}
this.stockchart.xAxis[0].setExtremes(fromTime, toTime);
} else {
var fromTime,
toTime;
if (e.min) {
fromTime = e.min;
} else {
fromTime = e.dataMin;
}
fromTime = Math.round(fromTime);
if (e.max) {
toTime = e.max;
} else {
toTime = e.dataMax;
}
toTime = Math.round(toTime);
this.settings.afterSetExtremes({startTimestamp: fromTime, endTimestamp: toTime});
}
};
Or see solution below and override the method.
There is no default API for that. Extend Highcharts via overriding drawInput function (your second code snippet).
There is a part of code that you should comment out or remove - the if clause after:
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
Example: http://jsfiddle.net/epL7awo4/1/
$(function () {
(function (H) {
H.wrap(H.RangeSelector.prototype, 'drawInput', function (proceed) {
var name = arguments[1],
merge = H.merge,
createElement = H.createElement,
PREFIX = 'highcharts-',
ABSOLUTE = 'absolute',
PX = 'px',
extend = H.extend,
pInt = H.pInt,
UNDEFINED;
//drawInput: function (name) {
var rangeSelector = this,
chart = rangeSelector.chart,
chartStyle = chart.renderer.style,
renderer = chart.renderer,
options = chart.options.rangeSelector,
defaultOptions = H.getOptions(),
lang = defaultOptions.lang,
div = rangeSelector.div,
isMin = name === 'min',
input,
label,
dateBox,
inputGroup = this.inputGroup;
// Create the text label
this[name + 'Label'] = label = renderer.label(lang[isMin ? 'rangeSelectorFrom' : 'rangeSelectorTo'], this.inputGroup.offset)
.attr({
padding: 2
})
.css(merge(chartStyle, options.labelStyle))
.add(inputGroup);
inputGroup.offset += label.width + 5;
// Create an SVG label that shows updated date ranges and and records click events that
// bring in the HTML input.
this[name + 'DateBox'] = dateBox = renderer.label('', inputGroup.offset)
.attr({
padding: 2,
width: options.inputBoxWidth || 90,
height: options.inputBoxHeight || 17,
stroke: options.inputBoxBorderColor || 'silver',
'stroke-width': 1
})
.css(merge({
textAlign: 'center',
color: '#444'
}, chartStyle, options.inputStyle))
.on('click', function () {
rangeSelector.showInput(name); // If it is already focused, the onfocus event doesn't fire (#3713)
rangeSelector[name + 'Input'].focus();
})
.add(inputGroup);
inputGroup.offset += dateBox.width + (isMin ? 10 : 0);
// Create the HTML input element. This is rendered as 1x1 pixel then set to the right size
// when focused.
this[name + 'Input'] = input = createElement('input', {
name: name,
className: PREFIX + 'range-selector',
type: 'text'
}, extend({
position: ABSOLUTE,
border: 0,
width: '1px', // Chrome needs a pixel to see it
height: '1px',
padding: 0,
textAlign: 'center',
fontSize: chartStyle.fontSize,
fontFamily: chartStyle.fontFamily,
top: chart.plotTop + PX // prevent jump on focus in Firefox
}, options.inputStyle), div);
// Blow up the input box
input.onfocus = function () {
rangeSelector.showInput(name);
};
// Hide away the input box
input.onblur = function () {
rangeSelector.hideInput(name);
};
// handle changes in the input boxes
input.onchange = function () {
var inputValue = input.value,
value = (options.inputDateParser || Date.parse)(inputValue),
xAxis = chart.xAxis[0],
dataMin = xAxis.dataMin,
dataMax = xAxis.dataMax;
// If the value isn't parsed directly to a value by the browser's Date.parse method,
// like YYYY-MM-DD in IE, try parsing it a different way
if (isNaN(value)) {
value = inputValue.split('-');
value = Date.UTC(pInt(value[0]), pInt(value[1]) - 1, pInt(value[2]));
}
if (!isNaN(value)) {
// Correct for timezone offset (#433)
if (!defaultOptions.global.useUTC) {
value = value + new Date().getTimezoneOffset() * 60 * 1000;
}
// Validate the extremes. If it goes beyound the data min or max, use the
// actual data extreme (#2438).
/* if (isMin) {
if (value > rangeSelector.maxInput.HCTime) {
value = UNDEFINED;
} else if (value < dataMin) {
value = dataMin;
}
} else {
if (value < rangeSelector.minInput.HCTime) {
value = UNDEFINED;
} else if (value > dataMax) {
value = dataMax;
}
}*/
// Set the extremes
if (value !== UNDEFINED) {
chart.xAxis[0].setExtremes(
isMin ? value : xAxis.min,
isMin ? xAxis.max : value,
UNDEFINED,
UNDEFINED, {
trigger: 'rangeSelectorInput'
});
}
}
};
//},
});
}(Highcharts));
/**
* Load new data depending on the selected min and max
*/
function afterSetExtremes(e) {
var chart = $('#container').highcharts();
chart.showLoading('Loading data from server...');
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?start=' + Math.round(e.min) +
'&end=' + Math.round(e.max) + '&callback=?', function (data) {
chart.series[0].setData(data);
chart.hideLoading();
});
}
// See source code from the JSONP handler at https://github.com/highslide-software/highcharts.com/blob/master/samples/data/from-sql.php
$.getJSON('http://www.highcharts.com/samples/data/from-sql.php?callback=?', function (data) {
// Add a null value for the end date
data = [].concat(data, [
[Date.UTC(2011, 9, 14, 19, 59), null, null, null, null]
]);
// create the chart
$('#container').highcharts('StockChart', {
chart: {
type: 'candlestick',
zoomType: 'x'
},
navigator: {
adaptToUpdatedData: false,
series: {
data: data
}
},
scrollbar: {
liveRedraw: false
},
title: {
text: 'AAPL history by the minute from 1998 to 2011'
},
subtitle: {
text: 'Displaying 1.7 million data points in Highcharts Stock by async server loading'
},
rangeSelector: {
buttons: [{
type: 'hour',
count: 1,
text: '1h'
}, {
type: 'day',
count: 1,
text: '1d'
}, {
type: 'month',
count: 1,
text: '1m'
}, {
type: 'year',
count: 1,
text: '1y'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: true,
selected: 4 // all
},
xAxis: {
events: {
afterSetExtremes: afterSetExtremes
},
minRange: 3600 * 1000 // one hour
},
yAxis: {
floor: 0
},
series: [{
data: data,
dataGrouping: {
enabled: false
}
}]
});
});
});
I'm trying to create a line diagram (datetime x-axis) with null values.
var rawData = [{
(...)
}, {
"PointOfTime": 1424991600,
"value": 6831.28806
}, {
"PointOfTime": 1425078000,
"value": null
}, {
"PointOfTime": 1425164400,
"value": null
}, {
(...)
}];
Adjust the data from a json source to an array:
rawData.forEach(function (d) {
var datetime = (d.PointOfTime + 3600) * 1000;
data.push([datetime, parseFloat(d.value)]);
});
As stated in the following fiddle, http://jsfiddle.net/wiesson/1m5hpLef there are no lines, only bullets. Any suggestions? I need the PointOfTime to create the range of the x-axis, even they are empty.
// Edit: As displayed in the following figure, the values in the future are unknown and not 0, therefore I would like to set them to null.
Add a condition, which check if your value is null. If yes then push this, instead of call parseFloat(null).
rawData.forEach(function (d) {
var datetime = d.PointOfTime * 1000;
if(d.value!==null)
data.push([datetime, parseFloat(d.value)]);
else
data.push([datetime, null]);
});
Example: http://jsfiddle.net/wiesson/1m5hpLef/