Creating Legend for a power BI custom visual - d3.js

I'm trying to create a power BI custom visual.I'm currently working on creating a pie chart.
The pie chart visual is being displayed.
I have defined Legend as a boolean property and currently trying to render a legend for the piechart when show property of Legend value is set totrue .
But even when settings.Legend.show is true, No Legend is being shown.
I'm using d3.js to create the visuals.
Can someone help?
Here's my visual.ts
module powerbi.extensibility.visual {
"use strict";
interface Data
{
quantity: number;
category: string;
color:string;
selectionId:ISelectionId;
}
interface PieChartArc {
datapoints:Data[];
setting:pieChartSettings;
dataview:DataView[];
}
interface pieChartSettings
{
Legend :
{
show:boolean;
};
}
interface LegendValues
{
category:string;
color:string;
selection:ISelectionId;
}
function visualTransform(options:VisualUpdateOptions,host:IVisualHost) : PieChartArc
{
let dataViews=options.dataViews;
let defaultSetting :pieChartSettings = {
Legend:{
show:false,
}
};
let viewModel:PieChartArc=
{
datapoints:[],
setting : defaultSetting,
dataview: dataViews
};
if (!dataViews
|| !dataViews[0]
|| !dataViews[0].categorical
|| !dataViews[0].categorical.categories
|| !dataViews[0].categorical.categories[0].source
|| !dataViews[0].categorical.values)
return viewModel;
let categorical=dataViews[0].categorical;
let category=categorical.categories[0];
let dataValue=categorical.values[0];
let pieChartData:Data[]=[];
let colorPalette:IColorPalette=host.colorPalette;
let objects=dataViews[0].metadata.objects
let pieChartSetting:pieChartSettings={
Legend:{
show: getValue<boolean>(objects,'Legend','show',defaultSetting.Legend.show)
}
}
for(let i=0;i<Math.max(category.values.length,dataValue.values.length);i++)
{
pieChartData.push({
quantity:<number>dataValue.values[i],
category:<string>category.values[i],
color:colorPalette.getColor(<string>category.values[i]).value,
selectionId:host.createSelectionIdBuilder()
.withCategory(category, i)
.createSelectionId()
});
console.log(pieChartData[i].color);
}
return {
datapoints:pieChartData,
setting:pieChartSetting,
dataview:dataViews
};
}
export class PieChart implements IVisual {
private target: HTMLElement;
private settings: VisualSettings;
private textNode: Text;
private pieChartContainer: d3.Selection<SVGElement>;
private arc: any;
private host: IVisualHost;
private selectionManager:ISelectionManager;
private svg: d3.Selection<SVGElement>;
private legend:d3.Selection<SVGElement>;
private g: any;
private pie: any;
private color: string;
private tooltipServiceWrapper: ITooltipServiceWrapper;
private pieSettings:pieChartSettings;
static data:DataView[];
static config=
{
solidOpacity:1,
transparentOpacity:0.5,
};
constructor(options: VisualConstructorOptions) {
console.log('Visual constructor', options);
this.target = options.element;
this.host=options.host;
this.selectionManager=options.host.createSelectionManager();
this.legend=d3.select(options.element);
let svg=this.svg=d3.select(options.element)
.append('svg')
.classed("pieChart",true)
;
this.pieChartContainer=svg.append('g').classed('pieChartContainer',true);//.attr('transform','translate('+100+',' +100+')');
this.tooltipServiceWrapper = createTooltipServiceWrapper(this.host.tooltipService, options.element);
}
public update(options: VisualUpdateOptions) {
// let legendModel : LegendValues[] = legendTransform(options,this.host);
let viewModel: PieChartArc = visualTransform(options, this.host);
let settings=this.pieSettings=viewModel.setting;
let category=options.dataViews[0].categorical.categories[0];
let dataValue=options.dataViews[0].categorical.values[0];
let legend=this.legend;
let legendEnumerate:LegendValues[];
// shows legend
if(settings.Legend.show)
{ //this.svg.append("text").text("Hello");
for(let i=0;i<category.values.length;++i)
{
legendEnumerate.push({
category:<string>category.values[i],
color:<string>dataValue.values[i],
selection:this.host.createSelectionIdBuilder()
.withCategory(category, i)
.createSelectionId()
});
}
let leg=this.legend.append('svg');
for(let i=0;i<Math.max(category.values.length,dataValue.values.length);++i)
{
// this.legend.html(legendEnumerate[i].category).attr("transform","translate(" + i*20 +",0)")
// leg.a
leg.append('circle')
.attr('cx',20)
.attr('cy',20)
.attr('r',8)
.attr('fill',legendEnumerate[i].color)
.attr("transform","translate(" + i*20 +",0)")
;
leg.append('text')
.text(legendEnumerate[i].category)
.attr("transform","translate(" + i*30 +",0)")
;
}
}
PieChart.data=options.dataViews;
let width=options.viewport.width;
let height=options.viewport.height;
let radius=Math.min(width,height)/2;
this.svg.attr({
width: width-20,
height: height-20,
});
this.arc=d3.svg.arc()
.innerRadius(0)
.outerRadius(radius-20);
this.pie = d3.layout.pie<Data>().value((d: Data):number => d.quantity).sort(null);
let fill = ((d):string=> d.data.color);
let tf = (d: Data) => `translate(${this.arc.centroid(d)})`;
let text = d => d.data.category;
this.svg.append('g');
this.g=this.svg.selectAll('.arc')
.data(this.pie(viewModel.datapoints))
.enter()
.append('g')
.attr('class', 'arc')
.data(this.pie(viewModel.datapoints))
// .attr("fill",fill)
;
let path= this.g.append('path')
.attr('d', this.arc)
.attr('fill',fill)
.attr('fill-opacity',1)
//.style("stroke","black")
.attr("stroke-width","0.8");
this.g.append('text').attr('transform', tf).text(text).attr('fill',"white");
this.tooltipServiceWrapper.addTooltip(path,
(tooltipEvent: TooltipEventArgs<number>) => PieChart.getTooltipData(tooltipEvent.data),
(tooltipEvent: TooltipEventArgs<number>) => null);
let selectionManager = this.selectionManager;
path.on("click",function(d)
{
// path.attr('fill','blue');
selectionManager.select(d.data.selectionId).then((ids: ISelectionId[])=>
{
path.attr('fill-opacity',ids.length>0? PieChart.config.transparentOpacity:PieChart.config.solidOpacity);
d3.select(this).attr('fill-opacity',PieChart.config.solidOpacity);
(<Event>d3.event).stopPropagation;
});
});
path.exit()
.remove();
}
private static parseSettings(dataView: DataView): VisualSettings {
return VisualSettings.parse(dataView) as VisualSettings;
}
/**
* This function gets called for each of the objects defined in the capabilities files and allows you to select which of the
* objects and properties you want to expose to the users in the property pane.
*
*/
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
// return VisualSettings.enumerateObjectInstances(this.settings || VisualSettings.getDefault(), options);
let objectName=options.objectName;
let objectEnumeration:VisualObjectInstance[]=[];
switch(objectName)
{
case 'Legend':
objectEnumeration.push({
objectName:objectName,
properties:{
show:this.pieSettings.Legend.show,
},
selector:null
});
};
return objectEnumeration;
}
private static getTooltipData(value: any): VisualTooltipDataItem[] {
return [{
displayName:PieChart.data[0].metadata.columns[1].displayName,
value: value.value.toString(),
color: value.data.color,
header:value.data.category
}];
}
}
}
here's my capabilities.json
{
"dataRoles": [
{
"displayName": "Category",
"name": "category",
"kind": "Grouping"
},
{
"displayName": "Measure",
"name": "measure",
"kind": "Measure"
}
],
"objects": {
"dataPoint": {
"displayName": "Data colors",
"properties": {
"defaultColor": {
"displayName": "Default color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"showAllDataPoints": {
"displayName": "Show all",
"type": {
"bool": true
}
},
"fill": {
"displayName": "Fill",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"fillRule": {
"displayName": "Color saturation",
"type": {
"fill": {}
}
},
"fontSize": {
"displayName": "Text Size",
"type": {
"formatting": {
"fontSize": true
}
}
}
}
},
"Legend":{
"displayName": "Legend",
"properties": {
"show" :{
"displayName": "Legend",
"type": {
"bool": true
}
}
}
}
},
"dataViewMappings": [
{
"categorical": {
"categories": {
"for": {
"in": "category"
},
"dataReductionAlgorithm": {
"top": {}
}
},
"values": {
"select": [
{
"bind": {
"to": "measure"
}
}
]
}
},
"conditions":[
{ "category":{
"max":1
},
"measure":{
"max":1
}
}
]
}
]
}

Related

Javascript - refactor array - array reduce

What is the best way to refactor(ise) an array / array of objects ?
I was thinking of using array.reduce, but could not figure out a solution.
for example, transform this array :
const carsInput =
[
{
brand: 'volkswagen',
model: 'golf',
motorSpec: {
...
},
Dim: {
...
},
},
...
{
brand: 'volkswagen',
model: 'passat',
motorSpec: {
...
},
Dim: {
...
},
},
]
to this array of objects
const carsOutput =
{
volkswagen: {
brandName: 'volkswagen',
models: {
golf: {
modelName: 'golf',
motorSpec: {
...
},
Dim: {
...
},
},
passat: {
modelName: 'passat',
motorSpec: {
...
},
Dim: {
...
},
},
},
},
}
Also if you think of another (better) way of presenting this array of objects, I am all ears !
Thanks a lot
I am sure others have more concise ways of doing this but here is one approach.
const carsInput =
[{
brand: 'ford',
model: 'mustang',
motorSpec: {
},
Dim: {
}
},
{
brand: 'volkswagen',
model: 'golf',
motorSpec: {
},
Dim: {
}
},
{
brand: 'volkswagen',
model: 'passat',
motorSpec: {
},
Dim: {
}
},
];
const carsOutput = new Object();
function initializeMake(makeName) {
carsOutput[makeName] = { brandName: makeName, models: {}};
return carsOutput[makeName]
}
carsInput.forEach(car => {
let make = carsOutput[car.brand] || initializeMake(car.brand);
const modelDefinition = {
'modelName': car['model'],
'motorSpec': car.motorSpec,
'Dim': car.Dim
};
make.models[car.model] = modelDefinition;
});
console.log(carsOutput)

Random horizontal line showing in d3 line chart?

I'm plotting data as a line chart and for some reason there are random horizontal lines going across one part of the graph:
I'm plotting my data like this:
const timeConv = d3.timeParse("%Y-%m-%dT%H:%M:%S.%f");
const width = 960;
const height = 500;
const margin = 5;
const padding = 5;
const adj = 30;
/* adding svg */
const svg = d3.select("#chart").append("svg")
.attr("preserveAspectRatio", "none")
.attr("viewBox", "-"
+ adj + " -"
+ adj + " "
+ (width + adj * 3) + " "
+ (height + adj * 3))
.attr("height", "100%")
.style("padding", padding)
.style("margin", margin)
/* sanitizing data */
const dataset = d3.json("formattedStats.json")
dataset.then(function(data) {
/* scaling */
const xScale = d3.scaleTime().range([0, width]);
const yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, d => timeConv(d.dateCreated)));
yScale.domain([
0,
d3.max(data, d => d.balance)
]);
const yaxis = d3.axisLeft()
.ticks(20)
.scale(yScale);
const xaxis = d3.axisBottom()
.ticks(d3.timeDay.filter(d => d3.timeDay.count(0, d) % 3 === 0))
.tickFormat(d3.timeFormat('%b %d'))
.scale(xScale);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xaxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0, 0)")
.call(yaxis);
const line = d3.line()
.x((d) => xScale(timeConv(d.dateCreated)))
.y((d) => yScale(d.balance));
svg.append("path")
.attr("d", line(data))
.attr("stroke", "black")
});
And my data looks like this:
[
{
"balance":1433,
"dateCreated":"2020-07-23T19:03:38.627"
},
{
"balance":1426,
"dateCreated":"2020-07-23T19:01:50.766"
},
{
"balance":1449,
"dateCreated":"2020-07-23T06:58:56.856"
},
{
"balance":1465,
"dateCreated":"2020-07-23T06:45:02.957"
},
{
"balance":1476,
"dateCreated":"2020-07-23T06:38:27.707"
},
{
"balance":1483,
"dateCreated":"2020-07-23T06:09:21.723"
},
{
"balance":1489,
"dateCreated":"2020-07-23T06:07:10.662"
},
{
"balance":1506,
"dateCreated":"2020-07-23T06:07:01.501"
},
{
"balance":1515,
"dateCreated":"2020-07-23T04:06:49.871"
},
{
"balance":1531,
"dateCreated":"2020-07-23T03:47:41.251"
},
{
"balance":1542,
"dateCreated":"2020-07-23T03:04:08.571"
},
{
"balance":1558,
"dateCreated":"2020-07-23T02:27:07.284"
},
{
"balance":1575,
"dateCreated":"2020-07-23T02:21:03.609"
},
{
"balance":1591,
"dateCreated":"2020-07-23T02:14:47.54"
},
{
"balance":1596,
"dateCreated":"2020-07-23T02:13:51.064"
},
{
"balance":1612,
"dateCreated":"2020-07-23T02:13:35.578"
},
{
"balance":1620,
"dateCreated":"2020-07-23T01:49:01.64"
},
{
"balance":1606,
"dateCreated":"2020-07-23T01:05:31.986"
},
{
"balance":1639,
"dateCreated":"2020-07-23T00:13:31.93"
},
{
"balance":1656,
"dateCreated":"2020-07-23T00:05:29.045"
},
{
"balance":1672,
"dateCreated":"2020-07-22T23:53:33.355"
},
{
"balance":1679,
"dateCreated":"2020-07-22T23:45:19.324"
},
{
"balance":1702,
"dateCreated":"2020-07-22T08:06:04.961"
},
{
"balance":1709,
"dateCreated":"2020-07-22T07:41:31.159"
},
{
"balance":949,
"dateCreated":"2020-07-22T06:11:06.519"
},
{
"balance":915,
"dateCreated":"2020-07-22T04:11:45.495"
},
{
"balance":932,
"dateCreated":"2020-07-22T03:31:21.528"
},
{
"balance":981,
"dateCreated":"2020-07-22T02:09:29.896"
},
{
"balance":965,
"dateCreated":"2020-07-22T02:08:44.348"
},
{
"balance":859,
"dateCreated":"2020-07-21T23:16:21.725"
},
{
"balance":837,
"dateCreated":"2020-07-21T22:11:51.734"
},
{
"balance":853,
"dateCreated":"2020-07-21T21:51:33.269"
},
{
"balance":869,
"dateCreated":"2020-07-21T21:25:00.833"
},
{
"balance":862,
"dateCreated":"2020-07-21T21:17:35.604"
},
{
"balance":839,
"dateCreated":"2020-07-21T20:30:21.715"
},
{
"balance":856,
"dateCreated":"2020-07-21T07:51:15.532"
},
{
"balance":899,
"dateCreated":"2020-07-21T07:10:07.262"
},
{
"balance":915,
"dateCreated":"2020-07-21T05:37:19.123"
},
{
"balance":910,
"dateCreated":"2020-07-21T05:33:59.065"
},
{
"balance":883,
"dateCreated":"2020-07-21T05:24:11.467"
},
{
"balance":871,
"dateCreated":"2020-07-21T03:21:38.474"
},
{
"balance":888,
"dateCreated":"2020-07-21T03:10:32.186"
},
{
"balance":891,
"dateCreated":"2020-07-21T03:05:09.042"
},
{
"balance":861,
"dateCreated":"2020-07-21T02:05:56.146"
},
{
"balance":878,
"dateCreated":"2020-07-21T01:43:27.761"
},
{
"balance":901,
"dateCreated":"2020-07-21T01:34:25.554"
},
{
"balance":907,
"dateCreated":"2020-07-20T23:18:39.558"
},
{
"balance":924,
"dateCreated":"2020-07-20T22:59:25.504"
},
{
"balance":940,
"dateCreated":"2020-07-20T21:01:51.96"
},
{
"balance":879,
"dateCreated":"2020-07-20T20:48:54.406"
},
{
"balance":857,
"dateCreated":"2020-07-20T19:56:13.648"
},
{
"balance":890,
"dateCreated":"2020-07-20T19:50:26.716"
},
{
"balance":906,
"dateCreated":"2020-07-20T19:00:18.921"
},
{
"balance":882,
"dateCreated":"2020-07-20T07:46:33.574"
},
{
"balance":896,
"dateCreated":"2020-07-20T07:07:40.55"
},
{
"balance":879,
"dateCreated":"2020-07-20T07:04:25.858"
},
{
"balance":906,
"dateCreated":"2020-07-20T06:52:07.748"
},
{
"balance":922,
"dateCreated":"2020-07-20T06:42:51.575"
},
{
"balance":927,
"dateCreated":"2020-07-20T06:42:31.973"
},
{
"balance":934.26,
"dateCreated":"2020-07-20T06:16:09.036"
},
{
"balance":902,
"dateCreated":"2020-07-20T05:31:56.692"
},
{
"balance":923,
"dateCreated":"2020-07-20T05:31:47.108"
},
{
"balance":894,
"dateCreated":"2020-07-20T05:22:20.871"
},
{
"balance":867,
"dateCreated":"2020-07-20T04:00:32.576"
},
{
"balance":883,
"dateCreated":"2020-07-20T04:00:26.06"
},
{
"balance":900,
"dateCreated":"2020-07-20T02:14:11.529"
},
{
"balance":916,
"dateCreated":"2020-07-20T01:38:55.908"
},
{
"balance":933,
"dateCreated":"2020-07-20T01:23:53.772"
},
{
"balance":949,
"dateCreated":"2020-07-20T01:23:45.458"
},
{
"balance":956,
"dateCreated":"2020-07-19T23:33:45.224"
},
{
"balance":962,
"dateCreated":"2020-07-19T23:08:56.22"
},
{
"balance":979,
"dateCreated":"2020-07-19T23:08:38.623"
},
{
"balance":880,
"dateCreated":"2020-07-19T23:06:48.195"
},
{
"balance":894,
"dateCreated":"2020-07-19T22:12:00.778"
},
{
"balance":877,
"dateCreated":"2020-07-19T21:57:13.62"
},
{
"balance":910,
"dateCreated":"2020-07-19T21:33:02.45"
},
{
"balance":925,
"dateCreated":"2020-07-19T21:25:22.116"
},
{
"balance":949,
"dateCreated":"2020-07-19T21:20:51.796"
},
{
"balance":965,
"dateCreated":"2020-07-19T20:59:27.897"
},
{
"balance":982,
"dateCreated":"2020-07-19T07:47:45.451"
},
{
"balance":822,
"dateCreated":"2020-07-19T07:44:31.419"
},
{
"balance":879,
"dateCreated":"2020-07-19T07:34:22.508"
},
{
"balance":797,
"dateCreated":"2020-07-19T06:46:47.993"
},
{
"balance":814,
"dateCreated":"2020-07-19T06:46:07.785"
},
{
"balance":820,
"dateCreated":"2020-07-19T06:36:24.427"
},
{
"balance":827,
"dateCreated":"2020-07-19T06:20:59.223"
},
{
"balance":861,
"dateCreated":"2020-07-19T06:19:46.962"
},
{
"balance":823,
"dateCreated":"2020-07-19T06:08:21.955"
},
{
"balance":840,
"dateCreated":"2020-07-19T06:07:30.056"
},
{
"balance":851,
"dateCreated":"2020-07-19T06:05:20.705"
},
{
"balance":849,
"dateCreated":"2020-07-19T05:39:32.269"
},
{
"balance":866.29,
"dateCreated":"2020-07-19T05:26:06.534"
},
{
"balance":825,
"dateCreated":"2020-07-19T05:13:44.842"
},
{
"balance":836,
"dateCreated":"2020-07-19T05:11:43.612"
},
{
"balance":852,
"dateCreated":"2020-07-19T05:04:52.876"
},
{
"balance":858,
"dateCreated":"2020-07-19T05:04:27.291"
},
{
"balance":874.54,
"dateCreated":"2020-07-19T05:04:12.409"
},
{
"balance":843,
"dateCreated":"2020-07-19T04:57:22.911"
},
{
"balance":859,
"dateCreated":"2020-07-19T04:52:21.601"
},
{
"balance":875.24,
"dateCreated":"2020-07-19T04:50:29"
},
{
"balance":847,
"dateCreated":"2020-07-19T04:45:57.354"
},
{
"balance":880,
"dateCreated":"2020-07-19T04:28:35.598"
},
{
"balance":897,
"dateCreated":"2020-07-19T04:17:26.469"
},
{
"balance":913,
"dateCreated":"2020-07-18T07:16:47.016"
},
{
"balance":947,
"dateCreated":"2020-07-18T06:37:00.094"
},
{
"balance":930,
"dateCreated":"2020-07-18T06:31:42.495"
},
{
"balance":952,
"dateCreated":"2020-07-17T09:08:22.177"
},
{
"balance":939.94,
"dateCreated":"2020-07-17T08:49:21.978"
},
{
"balance":918,
"dateCreated":"2020-07-17T08:34:31.417"
},
{
"balance":929,
"dateCreated":"2020-07-17T08:34:01.094"
},
{
"balance":935,
"dateCreated":"2020-07-16T07:08:20.004"
},
{
"balance":951,
"dateCreated":"2020-07-16T07:07:46.132"
},
{
"balance":968,
"dateCreated":"2020-07-14T04:39:32.887"
},
{
"balance":984,
"dateCreated":"2020-07-14T04:39:15.779"
},
{
"balance":1000,
"dateCreated":"2020-07-14T02:03:00.87"
},
{
"balance":1017,
"dateCreated":"2020-07-14T01:31:32.207"
},
{
"balance":1033,
"dateCreated":"2020-07-14T01:31:19.978"
},
{
"balance":1040,
"dateCreated":"2020-07-13T23:23:29.921"
},
{
"balance":1095.59,
"dateCreated":"2020-07-13T23:18:21.912"
},
{
"balance":1072,
"dateCreated":"2020-07-13T22:37:24.883"
},
{
"balance":1088,
"dateCreated":"2020-07-13T21:00:57.88"
},
{
"balance":1091,
"dateCreated":"2020-07-13T20:10:58.344"
},
{
"balance":1093.04,
"dateCreated":"2020-07-13T20:10:39.9"
},
{
"balance":1055,
"dateCreated":"2020-07-13T19:20:51.553"
},
{
"balance":1071,
"dateCreated":"2020-07-13T19:09:34.993"
},
{
"balance":1071,
"dateCreated":"2020-07-13T19:09:12.291"
},
{
"balance":1088.47,
"dateCreated":"2020-07-13T08:34:06.731"
},
{
"balance":1020.97,
"dateCreated":"2020-07-13T08:24:09.093"
},
{
"balance":999,
"dateCreated":"2020-07-13T08:12:13.226"
},
{
"balance":1015,
"dateCreated":"2020-07-13T07:49:26.818"
},
{
"balance":1032,
"dateCreated":"2020-07-13T07:40:00.178"
},
{
"balance":1114,
"dateCreated":"2020-07-12T23:23:32.335"
},
{
"balance":1154,
"dateCreated":"2020-07-12T21:01:59.128"
},
{
"balance":1169.97,
"dateCreated":"2020-07-12T14:39:57.463"
},
{
"balance":1156,
"dateCreated":"2020-07-12T14:01:02.792"
},
{
"balance":1172,
"dateCreated":"2020-07-12T13:55:36.566"
},
{
"balance":1177,
"dateCreated":"2020-07-12T13:55:19.394"
},
{
"balance":1156,
"dateCreated":"2020-07-12T08:24:33.968"
},
{
"balance":1121,
"dateCreated":"2020-07-12T08:08:41.735"
},
{
"balance":1089,
"dateCreated":"2020-07-12T06:48:28.84"
},
{
"balance":1106,
"dateCreated":"2020-07-12T05:40:55.865"
},
{
"balance":1036,
"dateCreated":"2020-07-11T22:45:14.995"
},
{
"balance":1012,
"dateCreated":"2020-07-11T21:52:56.498"
},
{
"balance":1028,
"dateCreated":"2020-07-11T21:36:53.419"
},
{
"balance":1043,
"dateCreated":"2020-07-11T09:20:19.116"
},
{
"balance":1037,
"dateCreated":"2020-07-11T08:27:01.816"
},
{
"balance":1043,
"dateCreated":"2020-07-11T08:26:45.696"
},
{
"balance":1066,
"dateCreated":"2020-07-11T08:26:30.808"
},
{
"balance":1070,
"dateCreated":"2020-07-11T08:08:32.191"
},
{
"balance":1081.18,
"dateCreated":"2020-07-11T08:04:44.88"
},
{
"balance":1049,
"dateCreated":"2020-07-11T06:59:57.157"
},
{
"balance":1066,
"dateCreated":"2020-07-11T06:29:55.306"
},
{
"balance":1082,
"dateCreated":"2020-07-11T06:29:37.378"
},
{
"balance":1089,
"dateCreated":"2020-07-10T07:20:38.373"
},
{
"balance":1120,
"dateCreated":"2020-07-10T07:04:02.663"
},
{
"balance":1131,
"dateCreated":"2020-07-10T07:02:00.364"
},
{
"balance":1148,
"dateCreated":"2020-07-10T05:50:07.739"
},
{
"balance":1164,
"dateCreated":"2020-07-10T05:24:33.478"
},
{
"balance":1108,
"dateCreated":"2020-07-10T05:23:31.012"
},
{
"balance":1083,
"dateCreated":"2020-07-10T04:10:02.153"
},
{
"balance":1099,
"dateCreated":"2020-07-10T04:09:37.055"
},
{
"balance":1114,
"dateCreated":"2020-07-10T02:40:05.837"
},
{
"balance":1095,
"dateCreated":"2020-07-10T02:13:02.745"
},
{
"balance":1106.16,
"dateCreated":"2020-07-10T02:10:33.916668"
},
{
"balance":1108,
"dateCreated":"2020-07-10T01:43:12.406"
},
{
"balance":1092,
"dateCreated":"2020-07-10T01:39:59.102"
},
{
"balance":1113,
"dateCreated":"2020-07-10T01:35:38.467"
},
{
"balance":1087,
"dateCreated":"2020-07-10T01:14:38.311"
},
{
"balance":1103,
"dateCreated":"2020-07-10T00:50:09.294"
},
{
"balance":1153,
"dateCreated":"2020-07-10T00:35:48.635"
},
{
"balance":1136,
"dateCreated":"2020-07-10T00:13:15.698"
},
{
"balance":1169,
"dateCreated":"2020-07-09T23:42:45.742"
},
{
"balance":1180,
"dateCreated":"2020-07-09T23:41:51.207"
},
{
"balance":1186,
"dateCreated":"2020-07-09T20:29:33.056"
},
{
"balance":1219,
"dateCreated":"2020-07-09T19:34:09.69"
},
{
"balance":1235,
"dateCreated":"2020-07-09T19:18:24.581"
},
{
"balance":1236,
"dateCreated":"2020-07-09T19:11:14.349"
},
{
"balance":1252,
"dateCreated":"2020-07-09T17:13:40.924"
},
{
"balance":1268,
"dateCreated":"2020-07-09T06:47:22.183"
},
{
"balance":1284,
"dateCreated":"2020-07-08T17:42:38.056"
},
{
"balance":1301,
"dateCreated":"2020-07-08T17:42:28.833"
},
{
"balance":1306.58,
"dateCreated":"2020-07-08T08:49:25.341"
},
{
"balance":1285.58,
"dateCreated":"2020-07-08T08:32:08.722"
},
{
"balance":1275,
"dateCreated":"2020-07-08T08:29:17.285"
},
{
"balance":1282,
"dateCreated":"2020-07-08T08:29:09.891"
},
{
"balance":1285,
"dateCreated":"2020-07-08T08:22:57.768"
},
{
"balance":1290,
"dateCreated":"2020-07-08T08:20:55.432"
},
{
"balance":1294,
"dateCreated":"2020-07-08T07:06:29.286"
},
{
"balance":1300,
"dateCreated":"2020-07-08T06:47:13.151"
},
{
"balance":1291,
"dateCreated":"2020-07-08T06:44:23.775"
},
{
"balance":1270,
"dateCreated":"2020-07-08T05:46:22.013"
},
{
"balance":1287,
"dateCreated":"2020-07-08T05:45:47.899"
},
{
"balance":1149,
"dateCreated":"2020-07-07T11:19:38.463"
},
{
"balance":1129.93,
"dateCreated":"2020-07-07T08:29:41.744"
},
{
"balance":1107,
"dateCreated":"2020-07-07T07:48:21.005"
},
{
"balance":1116,
"dateCreated":"2020-07-07T07:48:04.45"
},
{
"balance":1134,
"dateCreated":"2020-07-07T06:07:27.17"
},
{
"balance":1101,
"dateCreated":"2020-07-06T18:13:04.993"
},
{
"balance":1075,
"dateCreated":"2020-07-06T16:50:49.551"
},
{
"balance":1098,
"dateCreated":"2020-07-06T16:41:05.869"
},
{
"balance":1105,
"dateCreated":"2020-07-06T04:53:57.638"
},
{
"balance":1121,
"dateCreated":"2020-07-06T04:53:47.715"
},
{
"balance":1128,
"dateCreated":"2020-07-06T03:52:28.386"
},
{
"balance":1194,
"dateCreated":"2020-07-06T03:33:55.42"
},
{
"balance":1177,
"dateCreated":"2020-07-06T03:19:55.119"
},
{
"balance":1210,
"dateCreated":"2020-07-06T02:53:50.235"
},
{
"balance":1227,
"dateCreated":"2020-07-06T02:40:53.788"
},
{
"balance":1243,
"dateCreated":"2020-07-06T02:40:39.762"
},
{
"balance":1252,
"dateCreated":"2020-07-06T02:19:43.451"
},
{
"balance":1269,
"dateCreated":"2020-07-05T21:41:31.196"
},
{
"balance":1275,
"dateCreated":"2020-07-05T21:34:16.157"
},
{
"balance":1219,
"dateCreated":"2020-07-05T19:53:26.309"
},
{
"balance":1192,
"dateCreated":"2020-07-05T19:04:36.269"
},
{
"balance":1198,
"dateCreated":"2020-07-05T19:04:10.751"
},
{
"balance":1249,
"dateCreated":"2020-07-04T05:38:08.098"
},
{
"balance":1228,
"dateCreated":"2020-07-04T05:23:06.225"
},
{
"balance":1271,
"dateCreated":"2020-07-04T03:26:17.131"
},
{
"balance":1304,
"dateCreated":"2020-07-04T03:12:01.939"
},
{
"balance":1327,
"dateCreated":"2020-07-04T03:09:45.315"
},
{
"balance":1334,
"dateCreated":"2020-07-04T02:45:19.182"
},
{
"balance":1343,
"dateCreated":"2020-07-03T20:49:14.146"
},
{
"balance":1353.8,
"dateCreated":"2020-07-03T08:00:24.059"
},
{
"balance":1290,
"dateCreated":"2020-07-03T07:39:10.192"
},
{
"balance":1322,
"dateCreated":"2020-07-03T07:28:01.269"
},
{
"balance":1322.65,
"dateCreated":"2020-07-02T08:16:47.869"
},
{
"balance":1287,
"dateCreated":"2020-07-02T06:46:09.676"
},
{
"balance":1304,
"dateCreated":"2020-07-02T06:40:26.221"
},
{
"balance":1310,
"dateCreated":"2020-07-02T06:30:15.057"
},
{
"balance":1328,
"dateCreated":"2020-07-01T06:40:06.844"
},
{
"balance":1345,
"dateCreated":"2020-07-01T06:23:22.739"
},
{
"balance":1348,
"dateCreated":"2020-06-30T08:09:53.259"
},
{
"balance":1318,
"dateCreated":"2020-06-30T06:43:08.339"
},
{
"balance":1334,
"dateCreated":"2020-06-30T06:42:51.645"
}
]
Does anyone know why this might be happening? Thanks!
This sort of error (where the graph/chart line/rect/path/circle is largely correct but with one or two errant points) can generally be traced to one of two issues:
Errors in data
Errors in parsing the data
In your case, the data is fine but your parsing of it does not account for an edge case, when your time does not have milliseconds, compare:
const timeConv = d3.timeParse("%Y-%m-%dT%H:%M:%S.%f");
console.log(timeConv("2020-07-19T04:50:29"));
console.log(timeConv("2020-07-19T04:52:21.601"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
In your case, for one data point, d3.timeParse returns a null value in your case for one data point, causing a badly scaled x value for the path.
Two solutions come to mind, fix the data so this point is formatted as expected, or create a second time parser to account for this edge case:
.x((d) => xScale(timeConv(d.dateCreated) || timeConvEdgeCase(d.dateCreated)))
This seems hackier than ensuring consistently formatted data, but depending on your data source, this might need to be done inline or in loading the data in the browser anyways.
const data=[{"balance":1433,"dateCreated":"2020-07-23T19:03:38.627"},{"balance":1426,"dateCreated":"2020-07-23T19:01:50.766"},{"balance":1449,"dateCreated":"2020-07-23T06:58:56.856"},{"balance":1465,"dateCreated":"2020-07-23T06:45:02.957"},{"balance":1476,"dateCreated":"2020-07-23T06:38:27.707"},{"balance":1483,"dateCreated":"2020-07-23T06:09:21.723"},{"balance":1489,"dateCreated":"2020-07-23T06:07:10.662"},{"balance":1506,"dateCreated":"2020-07-23T06:07:01.501"},{"balance":1515,"dateCreated":"2020-07-23T04:06:49.871"},{"balance":1531,"dateCreated":"2020-07-23T03:47:41.251"},{"balance":1542,"dateCreated":"2020-07-23T03:04:08.571"},{"balance":1558,"dateCreated":"2020-07-23T02:27:07.284"},{"balance":1575,"dateCreated":"2020-07-23T02:21:03.609"},{"balance":1591,"dateCreated":"2020-07-23T02:14:47.54"},{"balance":1596,"dateCreated":"2020-07-23T02:13:51.064"},{"balance":1612,"dateCreated":"2020-07-23T02:13:35.578"},{"balance":1620,"dateCreated":"2020-07-23T01:49:01.64"},{"balance":1606,"dateCreated":"2020-07-23T01:05:31.986"},{"balance":1639,"dateCreated":"2020-07-23T00:13:31.93"},{"balance":1656,"dateCreated":"2020-07-23T00:05:29.045"},{"balance":1672,"dateCreated":"2020-07-22T23:53:33.355"},{"balance":1679,"dateCreated":"2020-07-22T23:45:19.324"},{"balance":1702,"dateCreated":"2020-07-22T08:06:04.961"},{"balance":1709,"dateCreated":"2020-07-22T07:41:31.159"},{"balance":949,"dateCreated":"2020-07-22T06:11:06.519"},{"balance":915,"dateCreated":"2020-07-22T04:11:45.495"},{"balance":932,"dateCreated":"2020-07-22T03:31:21.528"},{"balance":981,"dateCreated":"2020-07-22T02:09:29.896"},{"balance":965,"dateCreated":"2020-07-22T02:08:44.348"},{"balance":859,"dateCreated":"2020-07-21T23:16:21.725"},{"balance":837,"dateCreated":"2020-07-21T22:11:51.734"},{"balance":853,"dateCreated":"2020-07-21T21:51:33.269"},{"balance":869,"dateCreated":"2020-07-21T21:25:00.833"},{"balance":862,"dateCreated":"2020-07-21T21:17:35.604"},{"balance":839,"dateCreated":"2020-07-21T20:30:21.715"},{"balance":856,"dateCreated":"2020-07-21T07:51:15.532"},{"balance":899,"dateCreated":"2020-07-21T07:10:07.262"},{"balance":915,"dateCreated":"2020-07-21T05:37:19.123"},{"balance":910,"dateCreated":"2020-07-21T05:33:59.065"},{"balance":883,"dateCreated":"2020-07-21T05:24:11.467"},{"balance":871,"dateCreated":"2020-07-21T03:21:38.474"},{"balance":888,"dateCreated":"2020-07-21T03:10:32.186"},{"balance":891,"dateCreated":"2020-07-21T03:05:09.042"},{"balance":861,"dateCreated":"2020-07-21T02:05:56.146"},{"balance":878,"dateCreated":"2020-07-21T01:43:27.761"},{"balance":901,"dateCreated":"2020-07-21T01:34:25.554"},{"balance":907,"dateCreated":"2020-07-20T23:18:39.558"},{"balance":924,"dateCreated":"2020-07-20T22:59:25.504"},{"balance":940,"dateCreated":"2020-07-20T21:01:51.96"},{"balance":879,"dateCreated":"2020-07-20T20:48:54.406"},{"balance":857,"dateCreated":"2020-07-20T19:56:13.648"},{"balance":890,"dateCreated":"2020-07-20T19:50:26.716"},{"balance":906,"dateCreated":"2020-07-20T19:00:18.921"},{"balance":882,"dateCreated":"2020-07-20T07:46:33.574"},{"balance":896,"dateCreated":"2020-07-20T07:07:40.55"},{"balance":879,"dateCreated":"2020-07-20T07:04:25.858"},{"balance":906,"dateCreated":"2020-07-20T06:52:07.748"},{"balance":922,"dateCreated":"2020-07-20T06:42:51.575"},{"balance":927,"dateCreated":"2020-07-20T06:42:31.973"},{"balance":934.26,"dateCreated":"2020-07-20T06:16:09.036"},{"balance":902,"dateCreated":"2020-07-20T05:31:56.692"},{"balance":923,"dateCreated":"2020-07-20T05:31:47.108"},{"balance":894,"dateCreated":"2020-07-20T05:22:20.871"},{"balance":867,"dateCreated":"2020-07-20T04:00:32.576"},{"balance":883,"dateCreated":"2020-07-20T04:00:26.06"},{"balance":900,"dateCreated":"2020-07-20T02:14:11.529"},{"balance":916,"dateCreated":"2020-07-20T01:38:55.908"},{"balance":933,"dateCreated":"2020-07-20T01:23:53.772"},{"balance":949,"dateCreated":"2020-07-20T01:23:45.458"},{"balance":956,"dateCreated":"2020-07-19T23:33:45.224"},{"balance":962,"dateCreated":"2020-07-19T23:08:56.22"},{"balance":979,"dateCreated":"2020-07-19T23:08:38.623"},{"balance":880,"dateCreated":"2020-07-19T23:06:48.195"},{"balance":894,"dateCreated":"2020-07-19T22:12:00.778"},{"balance":877,"dateCreated":"2020-07-19T21:57:13.62"},{"balance":910,"dateCreated":"2020-07-19T21:33:02.45"},{"balance":925,"dateCreated":"2020-07-19T21:25:22.116"},{"balance":949,"dateCreated":"2020-07-19T21:20:51.796"},{"balance":965,"dateCreated":"2020-07-19T20:59:27.897"},{"balance":982,"dateCreated":"2020-07-19T07:47:45.451"},{"balance":822,"dateCreated":"2020-07-19T07:44:31.419"},{"balance":879,"dateCreated":"2020-07-19T07:34:22.508"},{"balance":797,"dateCreated":"2020-07-19T06:46:47.993"},{"balance":814,"dateCreated":"2020-07-19T06:46:07.785"},{"balance":820,"dateCreated":"2020-07-19T06:36:24.427"},{"balance":827,"dateCreated":"2020-07-19T06:20:59.223"},{"balance":861,"dateCreated":"2020-07-19T06:19:46.962"},{"balance":823,"dateCreated":"2020-07-19T06:08:21.955"},{"balance":840,"dateCreated":"2020-07-19T06:07:30.056"},{"balance":851,"dateCreated":"2020-07-19T06:05:20.705"},{"balance":849,"dateCreated":"2020-07-19T05:39:32.269"},{"balance":866.29,"dateCreated":"2020-07-19T05:26:06.534"},{"balance":825,"dateCreated":"2020-07-19T05:13:44.842"},{"balance":836,"dateCreated":"2020-07-19T05:11:43.612"},{"balance":852,"dateCreated":"2020-07-19T05:04:52.876"},{"balance":858,"dateCreated":"2020-07-19T05:04:27.291"},{"balance":874.54,"dateCreated":"2020-07-19T05:04:12.409"},{"balance":843,"dateCreated":"2020-07-19T04:57:22.911"},{"balance":859,"dateCreated":"2020-07-19T04:52:21.601"},{"balance":875.24,"dateCreated":"2020-07-19T04:50:29"},{"balance":847,"dateCreated":"2020-07-19T04:45:57.354"},{"balance":880,"dateCreated":"2020-07-19T04:28:35.598"},{"balance":897,"dateCreated":"2020-07-19T04:17:26.469"},{"balance":913,"dateCreated":"2020-07-18T07:16:47.016"},{"balance":947,"dateCreated":"2020-07-18T06:37:00.094"},{"balance":930,"dateCreated":"2020-07-18T06:31:42.495"},{"balance":952,"dateCreated":"2020-07-17T09:08:22.177"},{"balance":939.94,"dateCreated":"2020-07-17T08:49:21.978"},{"balance":918,"dateCreated":"2020-07-17T08:34:31.417"},{"balance":929,"dateCreated":"2020-07-17T08:34:01.094"},{"balance":935,"dateCreated":"2020-07-16T07:08:20.004"},{"balance":951,"dateCreated":"2020-07-16T07:07:46.132"},{"balance":968,"dateCreated":"2020-07-14T04:39:32.887"},{"balance":984,"dateCreated":"2020-07-14T04:39:15.779"},{"balance":1000,"dateCreated":"2020-07-14T02:03:00.87"},{"balance":1017,"dateCreated":"2020-07-14T01:31:32.207"},{"balance":1033,"dateCreated":"2020-07-14T01:31:19.978"},{"balance":1040,"dateCreated":"2020-07-13T23:23:29.921"},{"balance":1095.59,"dateCreated":"2020-07-13T23:18:21.912"},{"balance":1072,"dateCreated":"2020-07-13T22:37:24.883"},{"balance":1088,"dateCreated":"2020-07-13T21:00:57.88"},{"balance":1091,"dateCreated":"2020-07-13T20:10:58.344"},{"balance":1093.04,"dateCreated":"2020-07-13T20:10:39.9"},{"balance":1055,"dateCreated":"2020-07-13T19:20:51.553"},{"balance":1071,"dateCreated":"2020-07-13T19:09:34.993"},{"balance":1071,"dateCreated":"2020-07-13T19:09:12.291"},{"balance":1088.47,"dateCreated":"2020-07-13T08:34:06.731"},{"balance":1020.97,"dateCreated":"2020-07-13T08:24:09.093"},{"balance":999,"dateCreated":"2020-07-13T08:12:13.226"},{"balance":1015,"dateCreated":"2020-07-13T07:49:26.818"},{"balance":1032,"dateCreated":"2020-07-13T07:40:00.178"},{"balance":1114,"dateCreated":"2020-07-12T23:23:32.335"},{"balance":1154,"dateCreated":"2020-07-12T21:01:59.128"},{"balance":1169.97,"dateCreated":"2020-07-12T14:39:57.463"},{"balance":1156,"dateCreated":"2020-07-12T14:01:02.792"},{"balance":1172,"dateCreated":"2020-07-12T13:55:36.566"},{"balance":1177,"dateCreated":"2020-07-12T13:55:19.394"},{"balance":1156,"dateCreated":"2020-07-12T08:24:33.968"},{"balance":1121,"dateCreated":"2020-07-12T08:08:41.735"},{"balance":1089,"dateCreated":"2020-07-12T06:48:28.84"},{"balance":1106,"dateCreated":"2020-07-12T05:40:55.865"},{"balance":1036,"dateCreated":"2020-07-11T22:45:14.995"},{"balance":1012,"dateCreated":"2020-07-11T21:52:56.498"},{"balance":1028,"dateCreated":"2020-07-11T21:36:53.419"},{"balance":1043,"dateCreated":"2020-07-11T09:20:19.116"},{"balance":1037,"dateCreated":"2020-07-11T08:27:01.816"},{"balance":1043,"dateCreated":"2020-07-11T08:26:45.696"},{"balance":1066,"dateCreated":"2020-07-11T08:26:30.808"},{"balance":1070,"dateCreated":"2020-07-11T08:08:32.191"},{"balance":1081.18,"dateCreated":"2020-07-11T08:04:44.88"},{"balance":1049,"dateCreated":"2020-07-11T06:59:57.157"},{"balance":1066,"dateCreated":"2020-07-11T06:29:55.306"},{"balance":1082,"dateCreated":"2020-07-11T06:29:37.378"},{"balance":1089,"dateCreated":"2020-07-10T07:20:38.373"},{"balance":1120,"dateCreated":"2020-07-10T07:04:02.663"},{"balance":1131,"dateCreated":"2020-07-10T07:02:00.364"},{"balance":1148,"dateCreated":"2020-07-10T05:50:07.739"},{"balance":1164,"dateCreated":"2020-07-10T05:24:33.478"},{"balance":1108,"dateCreated":"2020-07-10T05:23:31.012"},{"balance":1083,"dateCreated":"2020-07-10T04:10:02.153"},{"balance":1099,"dateCreated":"2020-07-10T04:09:37.055"},{"balance":1114,"dateCreated":"2020-07-10T02:40:05.837"},{"balance":1095,"dateCreated":"2020-07-10T02:13:02.745"},{"balance":1106.16,"dateCreated":"2020-07-10T02:10:33.916668"},{"balance":1108,"dateCreated":"2020-07-10T01:43:12.406"},{"balance":1092,"dateCreated":"2020-07-10T01:39:59.102"},{"balance":1113,"dateCreated":"2020-07-10T01:35:38.467"},{"balance":1087,"dateCreated":"2020-07-10T01:14:38.311"},{"balance":1103,"dateCreated":"2020-07-10T00:50:09.294"},{"balance":1153,"dateCreated":"2020-07-10T00:35:48.635"},{"balance":1136,"dateCreated":"2020-07-10T00:13:15.698"},{"balance":1169,"dateCreated":"2020-07-09T23:42:45.742"},{"balance":1180,"dateCreated":"2020-07-09T23:41:51.207"},{"balance":1186,"dateCreated":"2020-07-09T20:29:33.056"},{"balance":1219,"dateCreated":"2020-07-09T19:34:09.69"},{"balance":1235,"dateCreated":"2020-07-09T19:18:24.581"},{"balance":1236,"dateCreated":"2020-07-09T19:11:14.349"},{"balance":1252,"dateCreated":"2020-07-09T17:13:40.924"},{"balance":1268,"dateCreated":"2020-07-09T06:47:22.183"},{"balance":1284,"dateCreated":"2020-07-08T17:42:38.056"},{"balance":1301,"dateCreated":"2020-07-08T17:42:28.833"},{"balance":1306.58,"dateCreated":"2020-07-08T08:49:25.341"},{"balance":1285.58,"dateCreated":"2020-07-08T08:32:08.722"},{"balance":1275,"dateCreated":"2020-07-08T08:29:17.285"},{"balance":1282,"dateCreated":"2020-07-08T08:29:09.891"},{"balance":1285,"dateCreated":"2020-07-08T08:22:57.768"},{"balance":1290,"dateCreated":"2020-07-08T08:20:55.432"},{"balance":1294,"dateCreated":"2020-07-08T07:06:29.286"},{"balance":1300,"dateCreated":"2020-07-08T06:47:13.151"},{"balance":1291,"dateCreated":"2020-07-08T06:44:23.775"},{"balance":1270,"dateCreated":"2020-07-08T05:46:22.013"},{"balance":1287,"dateCreated":"2020-07-08T05:45:47.899"},{"balance":1149,"dateCreated":"2020-07-07T11:19:38.463"},{"balance":1129.93,"dateCreated":"2020-07-07T08:29:41.744"},{"balance":1107,"dateCreated":"2020-07-07T07:48:21.005"},{"balance":1116,"dateCreated":"2020-07-07T07:48:04.45"},{"balance":1134,"dateCreated":"2020-07-07T06:07:27.17"},{"balance":1101,"dateCreated":"2020-07-06T18:13:04.993"},{"balance":1075,"dateCreated":"2020-07-06T16:50:49.551"},{"balance":1098,"dateCreated":"2020-07-06T16:41:05.869"},{"balance":1105,"dateCreated":"2020-07-06T04:53:57.638"},{"balance":1121,"dateCreated":"2020-07-06T04:53:47.715"},{"balance":1128,"dateCreated":"2020-07-06T03:52:28.386"},{"balance":1194,"dateCreated":"2020-07-06T03:33:55.42"},{"balance":1177,"dateCreated":"2020-07-06T03:19:55.119"},{"balance":1210,"dateCreated":"2020-07-06T02:53:50.235"},{"balance":1227,"dateCreated":"2020-07-06T02:40:53.788"},{"balance":1243,"dateCreated":"2020-07-06T02:40:39.762"},{"balance":1252,"dateCreated":"2020-07-06T02:19:43.451"},{"balance":1269,"dateCreated":"2020-07-05T21:41:31.196"},{"balance":1275,"dateCreated":"2020-07-05T21:34:16.157"},{"balance":1219,"dateCreated":"2020-07-05T19:53:26.309"},{"balance":1192,"dateCreated":"2020-07-05T19:04:36.269"},{"balance":1198,"dateCreated":"2020-07-05T19:04:10.751"},{"balance":1249,"dateCreated":"2020-07-04T05:38:08.098"},{"balance":1228,"dateCreated":"2020-07-04T05:23:06.225"},{"balance":1271,"dateCreated":"2020-07-04T03:26:17.131"},{"balance":1304,"dateCreated":"2020-07-04T03:12:01.939"},{"balance":1327,"dateCreated":"2020-07-04T03:09:45.315"},{"balance":1334,"dateCreated":"2020-07-04T02:45:19.182"},{"balance":1343,"dateCreated":"2020-07-03T20:49:14.146"},{"balance":1353.8,"dateCreated":"2020-07-03T08:00:24.059"},{"balance":1290,"dateCreated":"2020-07-03T07:39:10.192"},{"balance":1322,"dateCreated":"2020-07-03T07:28:01.269"},{"balance":1322.65,"dateCreated":"2020-07-02T08:16:47.869"},{"balance":1287,"dateCreated":"2020-07-02T06:46:09.676"},{"balance":1304,"dateCreated":"2020-07-02T06:40:26.221"},{"balance":1310,"dateCreated":"2020-07-02T06:30:15.057"},{"balance":1328,"dateCreated":"2020-07-01T06:40:06.844"},{"balance":1345,"dateCreated":"2020-07-01T06:23:22.739"},{"balance":1348,"dateCreated":"2020-06-30T08:09:53.259"},{"balance":1318,"dateCreated":"2020-06-30T06:43:08.339"},{"balance":1334,"dateCreated":"2020-06-30T06:42:51.645"}]
const timeConv = d3.timeParse("%Y-%m-%dT%H:%M:%S.%f");
const timeConvEdge = d3.timeParse("%Y-%m-%dT%H:%M:%S");
const width = 960;
const height = 500;
const margin = 5;
const padding = 5;
const adj = 30;
/* adding svg */
const svg = d3.select("#chart").append("svg")
.attr("preserveAspectRatio", "none")
.attr("viewBox", "-"
+ adj + " -"
+ adj + " "
+ (width + adj * 3) + " "
+ (height + adj * 3))
.attr("height", "100%")
.style("padding", padding)
.style("margin", margin)
/* scaling */
const xScale = d3.scaleTime().range([0, width]);
const yScale = d3.scaleLinear().rangeRound([height, 0]);
xScale.domain(d3.extent(data, d => timeConv(d.dateCreated) || timeConvEdge(d.dateCreated)));
yScale.domain([
0,
d3.max(data, d => d.balance)
]);
const yaxis = d3.axisLeft()
.ticks(20)
.scale(yScale);
const xaxis = d3.axisBottom()
.ticks(d3.timeDay.filter(d => d3.timeDay.count(0, d) % 3 === 0))
.tickFormat(d3.timeFormat('%b %d'))
.scale(xScale);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(xaxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0, 0)")
.call(yaxis);
const line = d3.line()
.x((d) => xScale(timeConv(d.dateCreated) || timeConvEdge(d.dateCreated)))
.y((d) => yScale(d.balance));
svg.append("path")
.attr("d", line(data))
.attr("stroke", "black")
path {
fill: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="chart">

Power BI visual will not plot, or show my added formatting options

I'm trying to work on this visual for Power BI but I am coming across a weird issue. This is supposed to be a bar chart (will convert to line) that supports a measure on both X and Y axis. When I try to plot with my data, I get an empty visual with no errors. My formatting options that I added (like tooltips or toggle options) don't even appear in the visual formatting options pane. I have messed around and I can not get this to work or really change at regardless of what I do, short of going in and throwing random characters into my code to break the syntax. I have even tried going to older visual files that I have tinkered with in the past and made sure that I'm not doing anything wrong, such as misusing modules, or placing them improperly. I even re-imported all of my modules, checked my variables, checked for typos, etc. I cannot seem to get this thing to work. I even tried removing the part in my capabilities that allows measures to be put into the axis to see if it would plot with normal value columns. To no avail, unfortunately.
Maybe I have stagnated eyes and I have been missing something obvious, or it's something more complicated than that.
I would greatly appreciate appreciate any help. Even if it's something unrelated to the issue.
"use strict";
import "#babel/polyfill";
import "./../style/visual.less";
import powerbi from "powerbi-visuals-api";
import VisualConstructorOptions = powerbi.extensibility.visual.VisualConstructorOptions;
import VisualUpdateOptions = powerbi.extensibility.visual.VisualUpdateOptions;
import IVisual = powerbi.extensibility.visual.IVisual;
import EnumerateVisualObjectInstancesOptions = powerbi.EnumerateVisualObjectInstancesOptions;
import VisualObjectInstance = powerbi.VisualObjectInstance;
import DataView = powerbi.DataView;
import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject;
import * as d3 from "d3";
import { VisualSettings } from "./settings";
import ISelectionManager = powerbi.extensibility.ISelectionManager;
import { ChartDataPoint, ChartViewModel } from "./viewmodels/model";
import IVisualHost = powerbi.extensibility.visual.IVisualHost;
import * as DataViewObject from 'powerbi-visuals-utils-dataviewutils';
interface DataPoints {
duration: number;
value: number;
details: number;
wells: string;
colour: string;
identity: powerbi.visuals.ISelectionId;
highlighted: boolean;
};
interface ViewModel {
dataPoints: DataPoints[];
maxValue: number;
highlights: boolean;
};
export class Visual implements IVisual {
private host: IVisualHost;
private svg: d3.Selection<SVGElement>;
private barGroup: d3.Selection<SVGElement>;
private viewModel: ViewModel;
private locale: string;
private selectionManager: ISelectionManager;
private xAxisGroup: d3.Selection<SVGElement>;
private yAxisGroup: d3.Selection<SVGElement>;
private settings = {
axis: {
x: {
padding: {
default: 50,
value: 50
},
show: {
default: true,
value: true
}
},
y: {
padding: {
default: 50,
value: 50
}
},
border: {
top: {
default: 10,
value: 10
}
}
}
}
constructor(options: VisualConstructorOptions) {
this.host = options.host;
this.svg = d3.select(options.element)
.append(".svg")
.classed("Visual", true);
this.barGroup = this.svg.append("g")
.classed("bar-group", true); //this was chart
this.xAxisGroup = this.svg.append("g")
.classed("x-axis", true);
this.selectionManager = this.host.createSelectionManager();
this.yAxisGroup = this.svg.append("g")
.classed("y-axis", true);
}
//This contains the 'canvas', its scaling, and how it can or cannot interact
public update(options: VisualUpdateOptions) {
//this.updateSettings(options);
let viewModel = this.getViewModel(options);
let width = options.viewport.width;
let height = options.viewport.height;
let xAxisPadding = this.settings.axis.x.show.value ? this.settings.axis.x.padding.value : 0;
// let yAxisPadding = this.settings.axis.y.show.value ? this.settings.axis.y.padding.value : 0;
this.svg.attr({
width: width,
height: height
});
let yScale = d3.scale.linear()
.domain([0, this.viewModel.maxValue])
.range([height - xAxisPadding, 0 + this.settings.axis.border.top.value]);
let xScale = d3.scale.linear()
.domain(viewModel.dataPoints.map(d => d.duration))
// .rangeRoundBands([yAxisPadding, width], this.xPadding);
let xAxis = d3.svg.axis() //come back to this later if it causes issues. https://www.youtube.com/watch?v=zLNfXxDsa-s&list=PL6z9i4iVbl8C2mtjFlH3ECb3q00eFDLAG&index=14 3:40 in
.scale(xScale)
.orient("bottom")
.tickSize(.5);
let yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.tickSize(.5);
this.xAxisGroup
.call(xAxis)
.attr({
transform: "translate(0 " + (height - xAxisPadding) + ")"
})
.style({
fill: "#777777"
})
.selectAll("text")
.attr({
"text-anchor": "end",
"font-size": "x-small"
});
this.yAxisGroup
.call(yAxis)
.attr({
transform: "translate(" + this.settings.axis.y.padding + ",0)"
})
.style({
fill: "#777777"
})
.selectAll("text")
.style({
"text-anchor": "end",
"font-size": "x-small"
});
let bars = this.barGroup
.selectAll(".bar") //keep an eye on this. was '.lines'
.data(viewModel.dataPoints);
bars.enter()
.append("svg")
.classed("bar", true); //this was chart
bars
.attr({
//width: xScale.range(),
height: d => height = yScale(d.value) - xAxisPadding,
x: d => xScale(d.duration),
})
.style({
fill: d => d.colour,
"fill-opacity": d => viewModel.highlights ? d.highlighted ? 1.0 : 0.5 : 1.0
})
.on("click", (d) => {
this.selectionManager
.select(d.identity, true)
.then(ids => {
bars.style({
"fill-opacity": ids.length > 0 ?
d => ids.indexOf(d.identity) >= 0 ? 1.0 : 0.5
: 1.0
});
})
});
bars.exit()
.remove();
}
/* private updateSettings(options: VisualUpdateOptions) {
this.settings.axis.x.show.value = DataViewObjects.getValue
(options.dataViews[0].metadata.objects, {
objectName: "xAxis",
propertyName: "show"
})
}*/
private getViewModel(options: VisualUpdateOptions): ViewModel {
let dv = options.dataViews;
let viewModel: ViewModel = {
dataPoints: [],
maxValue: 0,
highlights: false
};
/* if (!dv
|| !dv[0]
|| !dv[0].categorical
|| !dv[0].categorical.categories
|| !dv[0].categorical.categories[0].source
|| !dv[0].categorical.values)
return viewModel;*/
let view = dv[0].categorical;
let categories = view.categories[0];
let values = view.values[0];
let highlights = values.highlights;
for (let i = 0, len = Math.max(categories.values.length, values.values.length); i < len; i++) {
viewModel.dataPoints.push({
duration: <number>values.values[i],
value: <number>values.values[i],
details: <number>categories.values[i],
wells: <string>categories.values[i],
colour: this.host.colorPalette.getColor(<string>categories.values[i]).value,
identity: this.host.createSelectionIdBuilder()
.withCategory(categories, i)
.createSelectionId(),
highlighted: highlights ? highlights[i] ? true : false : false
});
}
viewModel.maxValue = d3.max(viewModel.dataPoints, d => d.value);
viewModel.highlights = viewModel.dataPoints.filter(d => d.highlighted).length > 0;
return viewModel;
}
public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstance[] | VisualObjectInstanceEnumerationObject {
let propertyGroupName = options.objectName;
let properties: VisualObjectInstance[] = [];
switch (propertyGroupName) {
case "xAxisGroup":
properties.push({
objectName: propertyGroupName,
properties: {
show: this.settings.axis.x.show.value
},
selector: null
});
break;
};
return properties
}
}
For anyone curious, my end goal (eventually) is to have a line plot that can support a measure (calculated column) in either axis. To give an example, dates are very common for the X axis. I would also like to be able to use a measure that can take a date range such as 9/5/2019-9/10/2019 and convert that to a duration. Output expected is 1-5.
The Y axis measure may do something like.. display all values that meet certain parameters, for instance.
{
"supportsHighlight": true,
"dataRoles": [
{
"displayName": "Y Axis",
"name": "values",
"kind": "Grouping"
},
{
"displayName": "Details",
"name": "details",
"kind": "Grouping"
},
{
"displayName": "Duration",
"name": "duration",
"kind": "Measure"
}
],
"objects": {
"xAxis": {
"displayName": "X Axis",
"properties": {
"show": {
"displayName": "Show X Axis",
"type": {
"bool": true
}
}
}
},
"dataPoint": {
"displayName": "Data colors",
"properties": {
"defaultColor": {
"displayName": "Default color",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"showAllDataPoints": {
"displayName": "Show all",
"type": {
"bool": true
}
},
"fill": {
"displayName": "Fill",
"type": {
"fill": {
"solid": {
"color": true
}
}
}
},
"fillRule": {
"displayName": "Color saturation",
"type": {
"fill": {}
}
},
"fontSize": {
"displayName": "Text Size",
"type": {
"formatting": {
"fontSize": true
}
}
}
}
}
},
"dataViewMappings": [
{
"categorical": {
"categories": {
"for": {
"in": "duration"
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
},
"values": {
"group": {
"by": "values",
"select": [
{
"bind": {
"to": "details"
}
}
]
},
"dataReductionAlgorithm": {
"top": {
"count": 450
}
}
}
}
}
]
}
Based on the comments, part of the challenge is getting a y-axis that plots the smallest number at the top and the largest number at the bottom. This is do-able with a custom format-string for a measure. Here is a quick and dirty mockup using a standard line chart.
If you set the measure format to Custom and use a format string without a '-' for negative, e.g. "0;0;0", the visual will not display a '-' in the y-axis. This leads to the effect you're looking for.
Note below that the measure is -1 * SUM ( 'Table'[Column1] ). That column contains only positive values.

nvd3 bar chart - show bars with value 0 too

Below is my Javascript
d3.json(sugarAPI, function(json) {
$("#id_rigdata svg").html("");
nv.addGraph(function() {
var height = 400;
var chart = nv.models.multiBarChart()
.showTitle(false)
.tooltips(true)
.showControls(false)
.showLegend(false)
.direction(app.lang.direction)
.colorData('data')
.color(['#C2B84D'])
.height(height)
.allowScroll(false)
.tooltipContent(function(key, x, y, e, graph) {
return '<p><b>' + y + '</b></p>';
});
chart.xAxis.axisLabel('Months');
chart.yAxis.axisLabel('Rig Count');
d3.selectAll("#id_rigdata svg > *").remove();
d3.select('#id_rigdata svg')
.datum(json)
.transition().duration(500)
.call(chart);
return chart;
});
});
Below is my JSON response
{
"properties":{
"title":"Monthly Count",
"labels":[
{
"group":1,
"l":"Jan"
},
{
"group":2,
"l":"Feb"
},
{
"group":3,
"l":"Mar"
},
{
"group":4,
"l":"Apr"
},
{
"group":5,
"l":"May"
},
{
"group":6,
"l":"Jun"
},
{
"group":7,
"l":"Jul"
},
{
"group":8,
"l":"Aug"
},
{
"group":9,
"l":"Sep"
},
{
"group":10,
"l":"Oct"
},
{
"group":11,
"l":"Nov"
},
{
"group":12,
"l":"Dec"
}
],
"values":[
{
"group":1,
"t":"0"
},
{
"group":2,
"t":"0"
},
{
"group":3,
"t":"26"
},
{
"group":4,
"t":"26"
},
{
"group":5,
"t":"0"
},
{
"group":6,
"t":"0"
},
{
"group":7,
"t":"0"
},
{
"group":8,
"t":"0"
},
{
"group":9,
"t":"0"
},
{
"group":10,
"t":"0"
},
{
"group":11,
"t":"0"
},
{
"group":12,
"t":"0"
}
]
},
"data":[
{
"key":"Monthly Rig Count",
"type":"bar",
"values":[
{
"series":0,
"x":1,
"y":0,
"y0":0
},
{
"series":0,
"x":2,
"y":0,
"y0":0
},
{
"series":0,
"x":3,
"y":26,
"y0":0
},
{
"series":0,
"x":4,
"y":26,
"y0":0
},
{
"series":0,
"x":5,
"y":0,
"y0":0
},
{
"series":0,
"x":6,
"y":0,
"y0":0
},
{
"series":0,
"x":7,
"y":0,
"y0":0
},
{
"series":0,
"x":8,
"y":0,
"y0":0
},
{
"series":0,
"x":9,
"y":0,
"y0":0
},
{
"series":0,
"x":10,
"y":0,
"y0":0
},
{
"series":0,
"x":11,
"y":0,
"y0":0
},
{
"series":0,
"x":12,
"y":0,
"y0":0
}
],
"color":"#C2B84D"
}
]
}
Output
Here I am trying to show the nvd3 barchart based on my JSON data. One bar for each Month. Now, only two bars are showing up where the value is not 0. What I need here is to show all the 12 bars with empty bars. I need to display the labels where the count is Month is 0 as well.

d3 v4 how to limit left/right panning on an x zoom line-graph

I am trying to display data in a line graph within a 24 hr period.
I have created a d3 v4 line graph, and have successfully:
Applied an x Axis only zoom, and redraw of all elements
Used clippath to keep my data from showing left/below of my axis lines
My issue is now trying to apply translateExtent to my d3.zoom. It seems to keep me from panning left/right before zooming, but when I zoom, it seems to cut off or add on a half hour or so to the end and beginning of my data set.
I think all I need to do is maybe add some code to my d3.zoom declaration, and maybe also add something into my zoom function.
I have tried adding .translateExtent([[0,0],[w,h]]) to my d3.zoom code and so many other variations that included subtracting/adding my margins, etc.
It looks like adding the w"2*margin like below takes away the jumping on initial click. (which confuses me because I would assume the first coordinate group should affect the left panning of my graph) but once I zoom in, it keeps me from panning left beyond the beginning of the day (yay) but allows me to pan a little past the end of the day (boo).
var zoomCall = d3.zoom()
.scaleExtent([1, 24]) // no zooming out (1x), only zoom to 1hr (24x)
.translateExtent([[0,0],[w+2*margin,h]])
.on("zoom", zoom);
I am also thinking in my zoom function of multiplying my width extent by d3.event.transform.k which should be the zoom level i believe, but it is not giving me what I want either:
function zoom() {
zoomCall.translateExtent([[0,0],[w+margin*2*d3.event.transform.k, h]]);
There also sometimes is a translation of my data left or right 20px just when clicking on the graph once after the translateExtent code is added.
window.onload = function() {
// parse the date / time functions
var formatTime = d3.timeFormat("%H:%M"),
formatMinutes = function(d) {
return formatTime(new Date(2012, 0, 1, 0, d));
};
// Get and Prepare Data for gap_array and range_array
var count_data = [
{
"date_time":"\/Date(1494864780000)\/",
"count":14
},
{
"date_time":"\/Date(1494864780000)\/",
"count":11
},
{
"date_time":"\/Date(1494864780000)\/",
"count":25
},
{
"date_time":"\/Date(1494864840000)\/",
"count":31
},
{
"date_time":"\/Date(1494864840000)\/",
"count":53
},
{
"date_time":"\/Date(1494864840000)\/",
"count":56
},
{
"date_time":"\/Date(1494864900000)\/",
"count":57
},
{
"date_time":"\/Date(1494864960000)\/",
"count":68
},
{
"date_time":"\/Date(1494865020000)\/",
"count":69
},
{
"date_time":"\/Date(1494865020000)\/",
"count":70
},
{
"date_time":"\/Date(1494865140000)\/",
"count":65
},
{
"date_time":"\/Date(1494865200000)\/",
"count":68
},
{
"date_time":"\/Date(1494865320000)\/",
"count":68
},
{
"date_time":"\/Date(1494865380000)\/",
"count":68
},
{
"date_time":"\/Date(1494865500000)\/",
"count":70
},
{
"date_time":"\/Date(1494865560000)\/",
"count":67
},
{
"date_time":"\/Date(1494865680000)\/",
"count":61
},
{
"date_time":"\/Date(1494865740000)\/",
"count":61
},
{
"date_time":"\/Date(1494865860000)\/",
"count":67
},
{
"date_time":"\/Date(1494865920000)\/",
"count":67
},
{
"date_time":"\/Date(1494866040000)\/",
"count":67
},
{
"date_time":"\/Date(1494866100000)\/",
"count":67
},
{
"date_time":"\/Date(1494866220000)\/",
"count":68
},
{
"date_time":"\/Date(1494866280000)\/",
"count":66
},
{
"date_time":"\/Date(1494866400000)\/",
"count":67
},
{
"date_time":"\/Date(1494866460000)\/",
"count":67
},
{
"date_time":"\/Date(1494866580000)\/",
"count":68
},
{
"date_time":"\/Date(1494866640000)\/",
"count":66
},
{
"date_time":"\/Date(1494866760000)\/",
"count":67
},
{
"date_time":"\/Date(1494866820000)\/",
"count":67
},
{
"date_time":"\/Date(1494866940000)\/",
"count":67
},
{
"date_time":"\/Date(1494867000000)\/",
"count":67
},
{
"date_time":"\/Date(1494867120000)\/",
"count":53
},
{
"date_time":"\/Date(1494867180000)\/",
"count":66
},
{
"date_time":"\/Date(1494867300000)\/",
"count":72
},
{
"date_time":"\/Date(1494867360000)\/",
"count":67
},
{
"date_time":"\/Date(1494867480000)\/",
"count":69
},
{
"date_time":"\/Date(1494867540000)\/",
"count":70
},
{
"date_time":"\/Date(1494867660000)\/",
"count":61
},
{
"date_time":"\/Date(1494867720000)\/",
"count":28
},
{
"date_time":"\/Date(1494867720000)\/",
"count":10
},
{
"date_time":"\/Date(1494867840000)\/",
"count":60
},
{
"date_time":"\/Date(1494867900000)\/",
"count":60
},
{
"date_time":"\/Date(1494868020000)\/",
"count":50
},
{
"date_time":"\/Date(1494868020000)\/",
"count":42
},
{
"date_time":"\/Date(1494868140000)\/",
"count":63
},
{
"date_time":"\/Date(1494868200000)\/",
"count":63
},
{
"date_time":"\/Date(1494868320000)\/",
"count":63
},
{
"date_time":"\/Date(1494868380000)\/",
"count":61
},
{
"date_time":"\/Date(1494868500000)\/",
"count":63
},
{
"date_time":"\/Date(1494868560000)\/",
"count":44
},
{
"date_time":"\/Date(1494868560000)\/",
"count":14
},
{
"date_time":"\/Date(1494868680000)\/",
"count":35
},
{
"date_time":"\/Date(1494868740000)\/",
"count":73
},
{
"date_time":"\/Date(1494868860000)\/",
"count":63
},
{
"date_time":"\/Date(1494868920000)\/",
"count":63
},
{
"date_time":"\/Date(1494869040000)\/",
"count":58
},
{
"date_time":"\/Date(1494869100000)\/",
"count":63
},
{
"date_time":"\/Date(1494869220000)\/",
"count":63
},
{
"date_time":"\/Date(1494869280000)\/",
"count":62
},
{
"date_time":"\/Date(1494869400000)\/",
"count":62
},
{
"date_time":"\/Date(1494869460000)\/",
"count":63
},
{
"date_time":"\/Date(1494869580000)\/",
"count":63
},
{
"date_time":"\/Date(1494869640000)\/",
"count":55
},
{
"date_time":"\/Date(1494869700000)\/",
"count":63
},
{
"date_time":"\/Date(1494869820000)\/",
"count":63
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494869880000)\/",
"count":58
},
{
"date_time":"\/Date(1494870000000)\/",
"count":35
},
{
"date_time":"\/Date(1494870060000)\/",
"count":43
},
{
"date_time":"\/Date(1494870120000)\/",
"count":53
},
{
"date_time":"\/Date(1494870180000)\/",
"count":0
},
{
"date_time":"\/Date(1494870180000)\/",
"count":6
},
{
"date_time":"\/Date(1494870180000)\/",
"count":8
},
{
"date_time":"\/Date(1494870240000)\/",
"count":0
},
{
"date_time":"\/Date(1494870240000)\/",
"count":4
},
{
"date_time":"\/Date(1494870240000)\/",
"count":0
},
{
"date_time":"\/Date(1494870240000)\/",
"count":6
},
{
"date_time":"\/Date(1494870300000)\/",
"count":0
},
{
"date_time":"\/Date(1494870300000)\/",
"count":6
},
{
"date_time":"\/Date(1494870300000)\/",
"count":15
},
{
"date_time":"\/Date(1494870300000)\/",
"count":14
},
{
"date_time":"\/Date(1494870300000)\/",
"count":22
},
{
"date_time":"\/Date(1494870360000)\/",
"count":27
},
{
"date_time":"\/Date(1494870420000)\/",
"count":63
},
{
"date_time":"\/Date(1494870540000)\/",
"count":63
},
{
"date_time":"\/Date(1494870600000)\/",
"count":67
},
{
"date_time":"\/Date(1494870720000)\/",
"count":63
},
{
"date_time":"\/Date(1494870780000)\/",
"count":63
},
{
"date_time":"\/Date(1494870900000)\/",
"count":54
},
{
"date_time":"\/Date(1494870900000)\/",
"count":0
},
{
"date_time":"\/Date(1494870900000)\/",
"count":6
},
{
"date_time":"\/Date(1494870960000)\/",
"count":15
},
{
"date_time":"\/Date(1494871020000)\/",
"count":13
},
{
"date_time":"\/Date(1494871020000)\/",
"count":0
},
{
"date_time":"\/Date(1494871080000)\/",
"count":0
},
{
"date_time":"\/Date(1494871200000)\/",
"count":0
},
{
"date_time":"\/Date(1494871260000)\/",
"count":0
},
{
"date_time":"\/Date(1494871380000)\/",
"count":0
},
{
"date_time":"\/Date(1494871440000)\/",
"count":0
},
{
"date_time":"\/Date(1494871560000)\/",
"count":0
},
{
"date_time":"\/Date(1494871620000)\/",
"count":0
},
{
"date_time":"\/Date(1494888600000)\/",
"count":6
},
{
"date_time":"\/Date(1494888600000)\/",
"count":6
},
{
"date_time":"\/Date(1494888720000)\/",
"count":0
},
{
"date_time":"\/Date(1494888780000)\/",
"count":0
},
{
"date_time":"\/Date(1494888900000)\/",
"count":0
},
{
"date_time":"\/Date(1494888960000)\/",
"count":0
},
{
"date_time":"\/Date(1494889080000)\/",
"count":0
},
{
"date_time":"\/Date(1494889140000)\/",
"count":0
},
{
"date_time":"\/Date(1494889260000)\/",
"count":0
},
{
"date_time":"\/Date(1494889320000)\/",
"count":0
},
{
"date_time":"\/Date(1494889380000)\/",
"count":4
},
{
"date_time":"\/Date(1494889380000)\/",
"count":4
},
{
"date_time":"\/Date(1494889380000)\/",
"count":0
},
{
"date_time":"\/Date(1494889380000)\/",
"count":5
},
{
"date_time":"\/Date(1494889380000)\/",
"count":0
},
{
"date_time":"\/Date(1494889500000)\/",
"count":0
},
{
"date_time":"\/Date(1494889560000)\/",
"count":0
},
{
"date_time":"\/Date(1494889680000)\/",
"count":0
},
{
"date_time":"\/Date(1494889740000)\/",
"count":4
},
{
"date_time":"\/Date(1494889740000)\/",
"count":0
},
{
"date_time":"\/Date(1494889860000)\/",
"count":0
},
{
"date_time":"\/Date(1494889920000)\/",
"count":0
},
{
"date_time":"\/Date(1494889920000)\/",
"count":4
},
{
"date_time":"\/Date(1494889980000)\/",
"count":0
},
{
"date_time":"\/Date(1494889980000)\/",
"count":0
},
{
"date_time":"\/Date(1494890220000)\/",
"count":5
},
{
"date_time":"\/Date(1494890220000)\/",
"count":5
},
{
"date_time":"\/Date(1494890280000)\/",
"count":6
},
{
"date_time":"\/Date(1494890340000)\/",
"count":0
},
{
"date_time":"\/Date(1494890400000)\/",
"count":0
},
{
"date_time":"\/Date(1494890520000)\/",
"count":0
},
{
"date_time":"\/Date(1494890580000)\/",
"count":0
},
{
"date_time":"\/Date(1494890700000)\/",
"count":0
},
{
"date_time":"\/Date(1494890760000)\/",
"count":0
},
{
"date_time":"\/Date(1494890880000)\/",
"count":0
},
];
count_data.forEach(function(d) {
d.date_time = new Date(parseInt(d.date_time.replace(/\/Date\((-?\d+)\)\//gi, "$1")));
//d.date_time = formatTime(d.date_time);
});
var maxY = d3.max(count_data, function(d) {
return d.count;
});
var minY = 0;
var minX = new Date(count_data[0].date_time).setHours(0, 0, 0, 0);
var maxX = new Date(count_data[0].date_time).setHours(23, 59, 59, 999);
var gap_array = [];
var range_array = [];
var range_start = moment(new Date(firstDateTime)),
range_end,
range_start_position = 0,
range_end_position,
in_range = true;
var gap_start,
gap_end,
gap_start_position,
gap_end_position,
in_gap = false,
gap_min = 2;
var firstDateTime = count_data[0].date_time;
var lastDateTime = count_data[count_data.length - 1].date_time;
var date_previous = moment(new Date(firstDateTime));
for (i = 0; i < count_data.length; i++) {
var current = moment(new Date(count_data[i].date_time));
if (current.diff(date_previous, "m") > gap_min && !in_gap) {
range_end = date_previous;
range_end_position = i - 1;
in_range = !in_range;
range_array.push({
start: range_start.toDate(),
end: range_end.toDate(),
start_position: range_start_position,
end_position: range_end_position
});
gap_start = date_previous;
gap_start_position = i;
in_gap = !in_gap;
} else if (current.diff(date_previous, "m") <= gap_min && in_gap) {
range_start_position = i;
range_start = current;
in_range = !in_range;
gap_end_position = i;
gap_end = current;
in_gap = !in_gap;
gap_array.push({
start: gap_start.toDate(),
end: gap_end.toDate(),
start_position: gap_start_position,
end_position: gap_end_position
});
} else if (i == count_data.length - 1) {
if (in_gap) {
gap_end_position = i;
gap_end = current;
in_gap = !in_gap;
gap_array.push({
start: gap_start.toDate(),
end: gap_end.toDate(),
start_position: gap_start_position,
end_position: gap_end_position
});
} else {
range_end = current;
range_end_position = i;
in_range = !in_range;
range_array.push({
start: range_start.toDate(),
end: range_end.toDate(),
start_position: range_start_position,
end_position: range_end_position
});
}
}
date_previous = current;
}
gap_array.push({
start: minX,
end: firstDateTime,
start_position: null,
end_position: 0
}); // push initial gap
gap_array.push({
start: lastDateTime,
end: maxX,
start_position: count_data.length - 1,
end_position: null
}); // push final gap
var count_data_array = [];
for (i = 0; i < range_array.length; i++) {
count_data_array.push(count_data.slice(range_array[i].start_position, range_array[i].end_position + (range_array[i].end_position < count_data.length - 1 ? 1 : 0)));
}
//
var margin = 40;
var w = document.getElementById("d3-chart").offsetWidth - margin - margin,
h = 250 - margin - margin;
var x = d3.scaleTime().range([0, w]).domain([minX, maxX]);
var y = d3.scaleLinear().range([h, 0]).domain([minY, maxY]);
var zoomCall = d3.zoom()
.scaleExtent([1, 24]) // no zooming out (1x), only zoom to 1hr (24x)
.on("zoom", zoom);
var container = d3.select("#d3-chart");
var svg = container.append("svg")
.attr("width", w + margin + margin)
.attr("height", h + margin + margin);
var g = svg.append("g")
.attr("transform",
"translate(" + margin + "," + margin + ")")
.call(zoomCall);
g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", w)
.attr("height", h);
var xAxis = d3.axisBottom(x).ticks(20); //.tickFormat(formatMinutes);
var yAxis = d3.axisLeft(y);
function zoom() {
// re-scale y axis during zoom;
xAxisCall.transition()
.duration(50)
.call(xAxis.scale(d3.event.transform.rescaleX(x)));
// re-draw circles using new y-axis scale;
var new_xScale = d3.event.transform.rescaleX(x);
var new_valueline = d3.line()
.x(function(d) {
return new_xScale(d.date_time);
})
.y(function(d) {
return y(d.count);
});
gaps.attr("x", d => new_xScale(d.start)).attr("width", d => new_xScale(d.end) - new_xScale(d.start)).attr("clip-path", "url(#clip)");
for (i = 0; i < paths.length; i++) {
paths[i]
.attr("d", new_valueline).attr("clip-path", "url(#clip)");
}
}
var gaps = g.selectAll('.gap')
.data(gap_array)
.enter().append('rect')
.attr("class", "gap")
.attr("x", d => x(d.start))
.attr("y", 0)
.attr("width", d => x(d.end) - x(d.start))
.attr("height", h)
.attr("fill", "#efefef");
var xAxisCall = g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
var yAxisCall = g.append("g")
.attr("class", "axis")
.call(yAxis);
// define the line
var valueline = d3.line()
.x(function(d) {
return x(d.date_time);
})
.y(function(d) {
return y(d.count);
});
var paths = [];
// Add the valueline path.
for (i = 0; i < count_data_array.length; i++) {
paths.push(
g.append("path")
.data([count_data_array[i]])
.attr("class", "line")
.attr("d", valueline)
);
}
g.append("rect")
.attr("class", "overlay")
.attr("width", w)
.attr("height", h)
};
#d3-chart {
position: relative;
}
.axis {
font: 14px sans-serif;
}
.line {
fill: none;
stroke: #3988ff;
stroke-width: 2px;
}
.overlay {
fill: none;
pointer-events: all;;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.13.0/moment.js"></script>
<h3>D3 Test</h3>
<div id="d3-chart"></div>
Not sure logically why, but the margin offset needs to be divided by the zoom factor.
function zoom() {
var zoomFactor = (d3.event != null ? d3.event.transform.k : 1);
zoomCall.translateExtent([[0,0],[w+((2*margin)/zoomFactor), h]]);

Resources