How to create horizontal multiline legend? - lightningchart

Is it possible to add a horizontal multi-line legend?
example

This can be done by creating 1 legend box for each row in the legend, and placing them in a column layout.
Here's a code snippet.
const { lightningChart, UIDraggingModes, emptyLine, emptyFill, UILayoutBuilders, UIOrigins, LegendBoxBuilders } = lcjs;
const chart = lightningChart().ChartXY()
.setTitle('')
.setPadding({ top: 110 })
const seriesList = new Array(11).fill(0).map((_, i) => {
const series = chart.addLineSeries()
.setName(`Trend #${i+1}`)
.addArrayY(new Array(20).fill(0).map(_ => Math.random()))
return series
})
const legendLayout = chart.addUIElement(UILayoutBuilders.Column)
.setPosition({ x: 0, y: 100 })
.setOrigin(UIOrigins.LeftTop)
.setMargin(10)
.setDraggingMode(UIDraggingModes.disabled)
const legendList = new Array(4).fill(0).map(_ => {
const legend = legendLayout.addElement(LegendBoxBuilders.HorizontalLegendBox)
.setTitle('')
.setMargin(0)
.setPadding(0)
return legend
})
legendList[0].add(seriesList[0])
legendList[0].add(seriesList[1])
legendList[0].add(seriesList[2])
legendList[0].add(seriesList[3])
legendList[1].add(seriesList[4])
legendList[1].add(seriesList[5])
legendList[1].add(seriesList[6])
legendList[2].add(seriesList[7])
legendList[2].add(seriesList[8])
legendList[2].add(seriesList[9])
legendList[3].add(seriesList[10])
<script src="http://unpkg.com/#arction/lcjs#3.1.0/dist/lcjs.iife.js"></script>

Related

Android Highchart bar chart add a text below a bar

I am displaying a bar chart in my android app with data labels. The highchart excepts only numeric value, although I have tried to use string as values the chart doesn't render. Below is my code
public void golyChartView(Number prev,Number curr)
{
HIOptions options = new HIOptions();
HIChart chart = new HIChart();
chart.setType("bar");
options.setChart(chart);
HITitle title = new HITitle();
title.setText("GOLY");
options.setTitle(title);
HISubtitle subtitle = new HISubtitle();
subtitle.setText("Growth Over Last Year");
options.setSubtitle(subtitle);
HIXAxis xaxis = new HIXAxis();
String[] categories = new String[] { "2020", "2021"};
xaxis.setCategories(new ArrayList<>(Arrays.asList(categories)));
options.setXAxis(new ArrayList<HIXAxis>(){{add(xaxis);}});
HIYAxis yaxis = new HIYAxis();
yaxis.setMin(0);
yaxis.setTitle(new HITitle());
yaxis.getTitle().setText("Sale Amount");
yaxis.getTitle().setAlign("high");
yaxis.setLabels(new HILabels());
yaxis.getLabels().setOverflow("justify");
options.setYAxis(new ArrayList<HIYAxis>(){{add(yaxis);}});
HITooltip tooltip = new HITooltip();
options.setTooltip(tooltip);
HILegend legend = new HILegend();
legend.setLayout("vertical");
legend.setAlign("right");
legend.setVerticalAlign("top");
legend.setX(-30);
legend.setY(40);
legend.setFloating(true);
legend.setBorderWidth(1);
legend.setBackgroundColor(HIColor.initWithHexValue("FFFFFF"));
legend.setShadow(true);
options.setLegend(legend);
HICredits credits = new HICredits();
credits.setEnabled(false);
options.setCredits(credits);
HIBar bar1 = new HIBar();
bar1.setName("Sale Value");
Number[] bar1Data = new Number[]{prev,curr};
bar1.setColorByPoint(true);
bar1.setData(new ArrayList<>(Arrays.asList(bar1Data)));
float numb = (((float)curr - (float)prev)/(float)prev)*100;
String percentage = String.valueOf(numb);
HIDataLabels dataLabels = new HIDataLabels();
dataLabels.setEnabled(true);
ArrayList<HIDataLabels> dataLabelsList = new ArrayList<>();
dataLabelsList.add(dataLabels);
bar1.setDataLabels(dataLabelsList);
HIDataLabels hiDataLabels = new HIDataLabels();
hiDataLabels.setEnabled(true);
HILegend hiLegend = new HILegend();
hiLegend.setEnabled(false);
options.setLegend(hiLegend);
options.setSeries(new ArrayList<>(Collections.singletonList(bar1)));
HIExporting exporting = new HIExporting();
exporting.setEnabled(false);
options.setExporting(exporting);
golyChart.setOptions(options);
golyChart.update(options,true,false);
}
Output
What I want to do?
As shown in the above code I have a string of percentage, that I want to add below the second bar Like below
I am stuck to it and don't know what to do
Any help would be highly appreciated.
You could use text Renderer to add text next to the bar.
I don't know android-highcharts but in javascript here an example
let customLabel,
labelInit = false;
Highcharts.chart('container', {
chart: {
type: 'bar',
events: {
render: function () {
const chart = this,
point = chart.series[0].data[1];
// prevent multiple label creation if used in render event but useless in "load" event
if (labelInit) customLabel.destroy();
customLabel = chart.renderer.text('+25 %', point.shapeArgs.height, point.tooltipPos[1] + chart.series[0].itemWidth + 10)
.add()
.attr({
zIndex: 3
});
labelInit = true;
}
}
},
title:{
text: "GOLY"
},
plotOptions: {
bar: {
grouping: false
}
},
xAxis: {
type: "category",
categories: ["2020", "2021"]
},
series: [{
data: [200, 400],
colorByPoint: true
}]
});

Discord.js adding rank feature to XP system using better-sqlite3

Im currently trying to add a rank feature to my already existing xp system. I can return a list from the database, in order of XP no problem, the issue is mapping and indexing. I know it should be simple but im struggling. any help would be greatly appreciated
const { MessageEmbed } = require("discord.js");
const { cyan } = require("../../colours.json")
const Discord = require("discord.js");
const botconfig = require("../../botconfig");
const SQLite = require("better-sqlite3");
const sql = new SQLite('../../scores.sqlite');
module.exports = {
config: {
name: "rank",
description: "Displays the users/#<users> current XP total & level",
usage: " | <user>",
category: "xp",
accessableby: "Members",
aliases: ["xp", "level"]
},
run: async (bot, message, args) => {
const member = message.mentions.members.first() || message.member || message.guild.members.cache.get(args[0])
score = bot.getScore.get(message.author.tag, message.guild.id);
console.log(score)
if (!score) {
score = {
id: `${message.guild.id}-${message.author.id}`,
user: message.author.tag,
guild: message.guild.id,
points: 0,
level: 1,
};
}
const data = sql.prepare("SELECT * FROM scores WHERE guild = ? ORDER BY points DESC;").all(message.guild.id);
let curxp = score.points;
let curlvl = score.level;
let nxtLvlXp = curlvl * 300;
let difference = nxtLvlXp - curxp;
const embed = new Discord.MessageEmbed()
.setTitle("XP / LEVEL")
.setDescription(member.user.tag)
.setThumbnail(member.user.displayAvatarURL())
.setColor(cyan)
.addField('**' + "Level" + '**', curlvl, true)
.addField('**' + "XP" + '**', curxp, true)
// .addField('**' + "Rank" + '**', rank, true)
.setFooter(`${difference} XP til next level up`, bot.user.displayAvatarURL());
return message.channel.send({ embed });
// message.reply(`You currently have ${score.points} points and are level ${score.level}!`);
}
}

Formatting date React

How can I arrange the date in the proper format?
On the screen the date wrong, it shows 32/03/2020 but day 32 does not exist.
The correct format date is on the image below.
I am new with Reactjs, what has gone wrong in my code and how can I solve it?
My code is below:
class Chamado extends Component {
constructor(props) {
super(props);
this.state = {
offset: 0,
pageCount: this.props.chamados.length / this.props.perPage,
};
this.formatDate = this.formatDate.bind(this);
this.handlePageClick = this.handlePageClick.bind(this);
}
formatDate(date) {
const dateObject = new Date(date);
const day =
dateObject.getDate() + 1 < 10
? '0' + (dateObject.getDate() + 1)
: dateObject.getDate() + 1;
const month =
dateObject.getMonth() + 1 < 10
? '0' + (dateObject.getMonth() + 1)
: dateObject.getMonth() + 1;
const year = dateObject.getFullYear();
const formattedString = `${day}/${month}/${year}`;
return formattedString;
}
handlePageClick(data) {
let selected = data.selected;
let offset = Math.ceil(selected * this.props.perPage);
this.setState({
offset: offset,
});
}
render() {
const props = this.props;
return (
<SC.Container>
<SC.Title>
Histórico de <b>Chamados</b>
{this.props.chamados.length !== undefined && (
<SC.Add
title="Abrir um novo chamado"
onClick={(event) => props.setViewAbrirChamados(true)}
>
<GoPlus size="21px" color="#FFF" />
</SC.Add>
)}
</SC.Title>
<SC.List>
{this.props.chamados.length !== undefined ? (
<React.Fragment>
{props.chamados.length > 0 &&
props.chamados.map((item, index) => {
const maxOffset = this.state.offset + (props.perPage - 1);
return (
index >= this.state.offset &&
index <= maxOffset && (
<SC.Item key={item.id}>
<SC.Info>
<SC.Details>
<p>
<b>
{item.id} |{' '}
{item.titulo || item.categoria.descricao}
</b>
</p>
<p>
{this.formatDate(item.data_abertura)} -{' '}
{item.hora_abertura}
</p>
</SC.Details>
</SC.Info>
<SC.Buttons
onClick={(event) => {
props.setViewChamadoInfo(true, item.id);
}}
>
<button>
<FaInfo size="24" color="#fff" />
</button>
</SC.Buttons>
</SC.Item>
)
The format is on the image below:
What you need is a padding function something like below
const padNumber = (number) => {
if (number.toString().length === 1) return `0${number}`;
return number;
};
const formatDate = (date) => {
const dateObject = new Date(date);
const day = dateObject.getDate();
const month = dateObject.getMonth() + 1;
const year = dateObject.getFullYear();
const formattedString = `${padNumber(day)}/${padNumber(month)}/${year}`;
return formattedString;
};
Still the easiest way would be using moment.js

DC.js histogram of crossfilter dimension counts

I have a crossfilter with the following data structure being inputted.
project | subproject | cost
data = [
["PrA", "SubPr1", 100],
["PrA", "SubPr2", 150],
["PrA", "SubPr3", 100],
["PrB", "SubPr4", 300],
["PrB", "SubPr5", 500],
["PrC", "SubPr6", 450]]
I can create a barchart that has the summed cost per project:
var ndx = crossfilter(data)
var projDim = ndx.dimension(function(d){return d.project;});
var projGroup = costDim.group().reduceSum(function(d){return d.budget;});
What I want to do is create a dc.js histogram by project cost...so {450: 2, 300: 1}, etc. As far as I can tell, crossfilter can have only attributes of each row be input for a dimension. Is there a way around this?
Accepting the challenge!
It is true, crossfilter does not support this kind of double-reduction, but if you are willing to accept a slight loss of efficiency, you can create "fake dimensions" and "fake groups" with the desired behavior. Luckily, dc.js doesn't use very much of the crossfilter API, so you don't have to implement too many methods.
The first part of the trick is to duplicate the dimension and group so that the new dimension and old dimension will each observe filtering on the other.
The second part is to create the fake groups and dimensions, which walk the bins of the copied group and rebin and refilter based on the values instead of the keys.
A start of a general solution is below. For some charts it is also necessary to implement group.top(), and it is usually okay to just forward that to group.all().
function values_dimension(dim, group) {
return {
filter: function(v) {
if(v !== null)
throw new Error("don't know how to do this!");
return dim.filter(null);
},
filterFunction: function(f) {
var f2 = [];
group.all().forEach(function(kv) {
if(f(kv.value))
f2.push(kv.key);
});
dim.filterFunction(function(k) {
return f2.indexOf(k) >= 0;
});
return this;
}
};
}
function values_group(group) {
return {
all: function() {
var byv = [];
group.all().forEach(function(kv) {
if(kv.value === 0)
return;
byv[kv.value] = (byv[kv.value] || 0) + 1;
});
var all2 = [];
byv.forEach(function(d, i) {
all2.push({key: i, value: d});
});
return all2;
}
};
}
// duplicate the dimension & group so each will observe filtering on the other
var projDim2 = ndx.dimension(function(d){return d.project;});
var projGroup2 = projDim2.group().reduceSum(function(d){return d.budget;});
var countBudgetDim = values_dimension(projDim2, projGroup2),
countBudgetGroup = values_group(projGroup2);
jsfiddle here: http://jsfiddle.net/gordonwoodhull/55zf7L1L/
JSFillde Link
Denormalize + Map-reduce. Note the data already include the cost per project as the 4th column ( and this can be pre-calculated easily). It's a hack, but hopefully an easy one in order to get DC.js and crossfilter works without too much change.
var data = [
["PrA", "SubPr1", 100, 450],
["PrA", "SubPr2", 150, 450],
["PrA", "SubPr3", 200, 450],
["PrB", "SubPr4", 300, 800],
["PrB", "SubPr5", 500, 800],
["PrC", "SubPr6", 450, 450]
];
var newdata = data.map(function (d) {
return {
project: d[0],
subproject: d[1],
budget: d[2],
cost: d[3]
};
})
var ndx = crossfilter(newdata),
costDim = ndx.dimension(function (d) {
return d.cost;
}),
visitedProj = {},
costGroup = costDim.group().reduce(function (p, v) {
if (visitedProj[v.project]) return p;
console.info(v.project);
visitedProj[v.project] = true;
return p + 1;
}, null, function () {
return 0;
});
dc.rowChart("#costChart")
.renderLabel(true)
.dimension(costDim)
.group(costGroup)
.xAxis().ticks(2);
dc.renderAll();
Map-Reduce can be very powerful and the API can be accessed from here. JSFillde Link

Simple line chart in DC and CrossFilter

I have some data like this
var data = [{date:'2013/01/01', claimNo:1},
{date:'2013/01/01', claimNo:2},
{date:'2013/01/02', claimNo:3}]
I want to plot a line chart in DC so that the days are on the X-Axis and the total # of claims are on the Y-Axis.
I have code like this
var ndx = crossfilter(data);
data.forEach(function (e) {
e.dd = dateFormat.parse(e.dd);
e.month = d3.time.month(e.dd);
});
var dateDim = ndx.dimension(function (d) {
return d.dd;
});
var datesClaimsGroup = dateDim.group();
var claimsLineChart = dc.lineChart("#claims-line-chart");
claimsLineChart
.width(200)
.height(40)
.renderArea(true)
.margins({ top: 0, left: -1, right: 2, bottom: 1 })
.group(datesClaimsGroup)
.dimension(dateDim)
.x(d3.time.scale().domain([data[0].dd, data[data.length - 1].dd]))
.title(function (d) {
return d.value;
});
The chart is plotted but the values in Y-Axis are the date occurance counts and not the claim counts. I know I am supposed to use a function to count the claims but I am not getting there.
For datesClaimsGroup you need to provide a reduce function to count the claims. Otherwise just .group() will default to an identity count reduce function as you observed.
var datesClaimsGroup = dateDim.group().reduceSum(...)
or
var datesClaimsGroup = dateDim.group().reduce(...)

Resources