Remove excess points from polygon sides in JavaScript / D3 - polygon

There is a random polygon where each side could have a random number of collinear points and, eventually, some of them lie between its ends and could be removed.
In other words, I need a function which go through polygon points and check if point lie within existing side it would be eliminated, so you would get
const output = [
{ x: 252, y: 475 },
{ x: 201, y: 303 },
{ x: 306, y: 206 },
{ x: 504, y: 215 },
{ x: 566, y: 292 },
{ x: 631, y: 303 },
{ x: 648, y: 434 },
{ x: 606, y: 535 },
{ x: 476, y: 636 },
{ x: 315, y: 642 },
{ x: 180, y: 546 }
];
from original set of points
const data = [
{ x: 216, y: 510.5 },
{ x: 252, y: 475 },
{ x: 201, y: 303 },
{ x: 306, y: 206 },
{ x: 359.6, y: 208.4 },
{ x: 417.4, y: 211.1 },
{ x: 504, y: 215 },
{ x: 566, y: 292 },
{ x: 598.5, y: 297.5 },
{ x: 631, y: 303 },
{ x: 648, y: 434 },
{ x: 606, y: 535 },
{ x: 577.5, y: 557.1 },
{ x: 553.2, y: 576.1 },
{ x: 521.5, y: 600.7 },
{ x: 476, y: 636 },
{ x: 395.5, y: 639 },
{ x: 315, y: 642 },
{ x: 268.2, y: 608.7 },
{ x: 253.9, y: 598.5 },
{ x: 218, y: 573 },
{ x: 180, y: 546 }
];
const data = [
{ x: 216, y: 510.5 },
{ x: 252, y: 475 },
{ x: 201, y: 303 },
{ x: 306, y: 206 },
{ x: 359.6, y: 208.4 },
{ x: 417.4, y: 211.1 },
{ x: 504, y: 215 },
{ x: 566, y: 292 },
{ x: 598.5, y: 297.5 },
{ x: 631, y: 303 },
{ x: 648, y: 434 },
{ x: 606, y: 535 },
{ x: 577.5, y: 557.1 },
{ x: 553.2, y: 576.1 },
{ x: 521.5, y: 600.7 },
{ x: 476, y: 636 },
{ x: 395.5, y: 639 },
{ x: 315, y: 642 },
{ x: 268.2, y: 608.7 },
{ x: 253.9, y: 598.5 },
{ x: 218, y: 573 },
{ x: 180, y: 546 }
];
const svg = d3.select("#scene"), colors = ["#005f73", "#0a9396", "#94d2bd", "#e9d8a6", "#ee9b00", "#ca6702", "#bb3e03", "#ae2012"];
let paths = svg.append("path")
.attr("class", "debug")
.attr("d", (id_) => generatePathFromPoints(data, true))
.attr("fill", colors[0]);
let dots = svg.selectAll(".circle")
.data(data)
.enter()
.append("g")
.attr("transform", d_ => `translate(${d_.x},${d_.y})`)
dots.append("circle")
.attr("cx", 0)
.attr("cy", 0)
.attr("r", 3);
dots.append("text")
.attr("x", -4)
.attr("y", -4)
.attr("font-family", "arial")
.attr("font-size", 10)
.text((d_, i_) => i_);
function generatePathFromPoints(points_, closed_){
let d = `M${points_[0].x} ${points_[0].y}`;
for(let i = 1; i < points_.length; i++) { d += `L${points_[i].x} ${points_[i].y}`; }
if(closed_) { d += "Z"; }
return d;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg id="scene" viewBox="0 0 800 800" preserveAspectRatio="xMinYMin meet"></svg>

You need to loop through all points and check each side's angle.
A side would be defined by these points:
p1 = data[i];
p2 = data[i+1];
The angles are calculated like so
let angle = (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;
If the angle changes - push it to a new point array.
let dataNew = removeInbetweenPoints(data);
// render original polygon
let newPolyO = [];
data.forEach((p) => {
newPolyO.push(p.x, p.y);
});
polygonOrig.setAttribute("points", newPolyO.join(" "));
// render reduced polygon
let newPoly = [];
dataNew.forEach((p) => {
newPoly.push(p.x, p.y);
});
polygon.setAttribute("points", newPoly.join(" "));
// remove inbetween poly vertices
function removeInbetweenPoints(points) {
let pointsNew = [];
let currentAngle = +getAngle(points[0], points[1]).toFixed(0);
for (i = 0; i < points.length; i++) {
let p1 = points[i];
// p2 gets get first point if last index
let n = points[i + 1] ? i + 1 : 0;
let p2 = points[n];
let angle = +getAngle(p1, p2).toFixed(0);
// angle change - add vertex to reduced point array
if (angle != currentAngle || i == 0) {
pointsNew.push(p1);
// update current angle for next segment checks
currentAngle = angle;
}
// remove start if it intersects with first side
if (i == points.length - 1) {
let pLast = points[points.length - 1];
let pSecond = points[1];
let angle2 = +getAngle(pLast, pSecond).toFixed(0);
pointsNew.shift();
}
}
return pointsNew;
}
// get angle helper
function getAngle(p1, p2) {
let angle = (Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180) / Math.PI;
return angle;
}
svg {
width: 45vw;
border: 1px solid #ccc;
overflow: visible;
margin: 0.5vw;
}
polygon {
marker-start: url(#markerStart);
marker-mid: url(#markerRound);
stroke: transparent;
stroke-width: 0.25%;
}
<svg id="svgOrig" viewBox="175.6 201.6 476.8 444.8">
<polygon id="polygonOrig" points="216 510.5 252 475 201 303 306 206 359.6 208.4 417.4 211.1 504 215 566 292 598.5 297.5 631 303 648 434 606 535 577.5 557.1 553.2 576.1 521.5 600.7 476 636 395.5 639 315 642 268.2 608.7 253.9 598.5 218 573 180 546"></polygon>
</svg>
<svg id="svg" viewBox="175.6 201.6 476.8 444.8">
<polygon id="polygon" fill="green"></polygon>
</svg>
<!-- markers to show commands -->
<svg id="svgMarkers" style="width:0; height:0; position:absolute; z-index:-1;float:left;">
<defs>
<marker id="markerStart" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" orient="auto-start-reverse">
<circle cx="5" cy="5" r="3.5" fill="purple" />
</marker>
<marker id="markerRound" overflow="visible" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="strokeWidth" markerWidth="10" markerHeight="10" orient="auto-start-reverse">
<circle cx="5" cy="5" r="3.5" fill="red" />
</marker>
</defs>
</svg>
<script>
const data = [{
x: 216,
y: 510.5
},
{
x: 252,
y: 475
},
{
x: 201,
y: 303
},
{
x: 306,
y: 206
},
{
x: 359.6,
y: 208.4
},
{
x: 417.4,
y: 211.1
},
{
x: 504,
y: 215
},
{
x: 566,
y: 292
},
{
x: 598.5,
y: 297.5
},
{
x: 631,
y: 303
},
{
x: 648,
y: 434
},
{
x: 606,
y: 535
},
{
x: 577.5,
y: 557.1
},
{
x: 553.2,
y: 576.1
},
{
x: 521.5,
y: 600.7
},
{
x: 476,
y: 636
},
{
x: 395.5,
y: 639
},
{
x: 315,
y: 642
},
{
x: 268.2,
y: 608.7
},
{
x: 253.9,
y: 598.5
},
{
x: 218,
y: 573
},
{
x: 180,
y: 546
}
];
</script>

Related

Chart.js: grid lines only on dataset points

I created a simple chart:
const myChart = new Chart(document.getElementById('myChartChart').getContext('2d');, {
type: 'scatter',
data: {
datasets: [{
label: 'Dataset',
data: [{
x: -40,
y: 34,
}, {
x: -10,
y: 45,
}, {
x: 140,
y: 45,
}],
fill: true,
stepped: true,
}]
},
options: {
responsive: true,
scales: {
x: {
suggestedMin: -50,
suggestedMax: 150,
ticks: {
callback: function(value, index, values) {
return value + ' °C';
}
},
},
y: {
suggestedMin: 32,
suggestedMax: 46,
ticks: {
callback: function(value, index, values) {
return value + ' bar';
}
},
}
},
plugins: {
legend: {
display: false
}
}
}
});
Fiddle: https://jsfiddle.net/o6b97xL2
But I don't like to have static periodical grid lines and labels but only on my three data points.
With callbacks I only get the defined steps from the grid. How can I achieve something like this:
Image Link (You need at least 10 reputation to post images)
You can define afterBuildTicks callbacks on both axes. In there, you replace the axis ticks with your own ticks extracted from your data.
Please take a look at your amended code and see how it works.
const data = [
{ x: -40, y: 34 },
{ x: -10, y: 45 },
{ x: 140, y: 45 }
];
myChart = new Chart('myChartChart', {
type: 'scatter',
data: {
datasets: [{
label: 'Dataset',
data: data,
fill: true,
stepped: true,
}]
},
options: {
responsive: true,
scales: {
x: {
suggestedMin: -50,
suggestedMax: 150,
afterBuildTicks: axis => axis.ticks = data.map(v => ({ value: v.x })),
ticks: {
callback: v => v + ' °C'
}
},
y: {
suggestedMin: 32,
suggestedMax: 46,
afterBuildTicks: axis => axis.ticks = data.map(v => ({ value: v.y })),
ticks: {
callback: v => v + ' bar'
}
}
},
plugins: {
legend: {
display: false
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.2/chart.min.js"></script>
<canvas id="myChartChart"></canvas>

How do I implement asynchronous drilldown in area charts. Below is my code

Below is my code for asynchronous drill down in area charts. The drill down is not happening. Does anyone know how if this is the right way to do it? I have implement the asynchronous drill down as shown below. Can you suggest how to implement asynchronous drill down in area charts? Your help would be appreciated.
export class HomePage {
chartOptions : any;
constructor(public navCtrl: NavController) {
this.chartOptions = {
chart: {
type: 'area',
events: {
//Asynchronous drill down
drilldown: function (e) {
if (!e.seriesOptions) {
var chart = this,
drilldown = {
'USA': {
name: 'USA',
data: [1, 2, 3, 4, 1, 6, 7, 8],
},
},
series = drilldown[e.point.name];
// Show the loading label
chart.showLoading('Simulating Ajax ...');
setTimeout(function () {
chart.hideLoading();
chart.addSeriesAsDrilldown(e.point, series);
}, 1000);
}
}
}
},
title: {
text: 'Operations'
},
xAxis: {
allowDecimals: false,
labels: {
formatter: function () {
return this.value; // clean, unformatted number for year
}
}
},
yAxis: {
title: {
text: 'Nuclear weapon states'
},
labels: {
formatter: function () {
return this.value / 1000 + 'k';
}
}
},
tooltip: {
pointFormat: '{series.name} had stockpiled <b>{point.y:,.0f}</b><br/>warheads in {point.x}'
},
plotOptions: {
area: {
pointStart: 1940,
marker: {
enabled: false,
symbol: 'circle',
radius: 2,
states: {
hover: {
enabled: true,
}
}
}
}
},
series: [{
name: 'USA',
// colorByPoint: true,
trackByArea: true,
data: [{
y: null,
drilldown: 'true'
}, {
y: null,
drilldown: 'true'
}, {
y: null,
drilldown: 'true'
},
{ y: null,
drilldown: 'true'
}, {
y: null,
drilldown: 'true'
}, {
y: 6,
drilldown: 'true'
}, {
y: 11,
drilldown: 'true'
},{
y: 32,
drilldown: 'true'
}, {
y: 110,
drilldown: 'true'
}, {
y: 235,
drilldown: 'true'
}, {
y: 369,
drilldown: 'true'
}, {
y: 640,
drilldown: 'true'
},{
y: 1005,
drilldown: 'true'
}, {
y: 1436,
drilldown: 'true'
}, {
y: 2063,
drilldown: 'true'
}, {
y: 3057,
drilldown: 'true'
},{
y: 4618,
drilldown: 'true'
}, {
y: 6444,
drilldown: 'true'
}, {
y: 9822,
drilldown: 'true'
}, {
y: 15468,
drilldown: 'true'
},{
y: 20434,
drilldown: 'true'
}, {
y: 24126,
drilldown: 'true'
}, {
y: 27387,
drilldown: 'true'
}, {
y: 29459,
drilldown: 'true'
}, {
y: 27387,
drilldown: 'true'
}, {
y: 29459,
drilldown: 'true'
},{
y: 32040,
drilldown: 'true'
}, {
y: 31233,
drilldown: 'true'
},{
y: 29224,
drilldown: 'true'
}, {
y: 27342,
drilldown: 'true'
}, {
y: 26662,
drilldown: 'true'
}, {
y: 26956,
drilldown: 'true'
},{
y: 27912,
drilldown: 'true'
}, {
y: 28999,
drilldown: 'true'
}, {
y: 28965,
drilldown: 'true'
}, {
y: 27826,
drilldown: 'true'
},{
y: 25579,
drilldown: 'true'
}, {
y: 25722,
drilldown: 'true'
}, {
y: 24826,
drilldown: 'true'
},{
y: 24605,
drilldown: 'true'
}, {
y: 24304,
drilldown: 'true'
}, {
y: 23464,
drilldown: 'true'
}, {
y: 23708,
drilldown: 'true'
},{
y: 24099,
drilldown: 'true'
}, {
y: 24357,
drilldown: 'true'
}, {
y: 24237,
drilldown: 'true'
}, {
y: 24401,
drilldown: 'true'
},{
y: 24344,
drilldown: 'true'
}, {
y: 23586,
drilldown: 'true'
}, {
y: 22380,
drilldown: 'true'
}, {
y: 21004,
drilldown: 'true'
},{
y: 17287,
drilldown: 'true'
}, {
y: 14747,
drilldown: 'true'
}, {
y: 13076,
drilldown: 'true'
}, {
y: 12555,
drilldown: 'true'
},{
y: 12144,
drilldown: 'true'
}, {
y: 11009,
drilldown: 'true'
}, {
y: 10950,
drilldown: 'true'
}, {
y: 10871,
drilldown: 'true'
},{
y: 10824,
drilldown: 'true'
}, {
y: 10577,
drilldown: 'true'
}, {
y: 10527,
drilldown: 'true'
}, {
y: 10475,
drilldown: 'true'
},{
y: 10421,
drilldown: 'true'
}, {
y: 10358,
drilldown: 'true'
}, {
y: 10295,
drilldown: 'true'
}, {
y: 10104,
drilldown: 'true'
},{
y: 9914,
drilldown: 'true'
}, {
y: 9620,
drilldown: 'true'
}, {
y: 9326,
drilldown: 'true'
}, {
y: 5113,
drilldown: 'true'
},{
y: 5113,
drilldown: 'true'
}, {
y: 4954,
drilldown: 'true'
}, {
y: 4804,
drilldown: 'true'
}, {
y: 4761,
drilldown: 'true'
}, {
y: 4717,
drilldown: 'true'
},{
y: 4368,
drilldown: 'true'
}, {
y: 4018,
drilldown: 'true'
}]
}, {
name: 'USSR/Russia',
data: [null, null, null, null, null, null, null, null, null, null,
5, 25, 50, 120, 150, 200, 426, 660, 869, 1060,
1605, 2471, 3322, 4238, 5221, 6129, 7089, 8339, 9399, 10538,
11643, 13092, 14478, 15915, 17385, 19055, 21205, 23044, 25393, 27935,
30062, 32049, 33952, 35804, 37431, 39197, 45000, 43000, 41000, 39000,
37000, 35000, 33000, 31000, 29000, 27000, 25000, 24000, 23000, 22000,
21000, 20000, 19000, 18000, 18000, 17000, 16000, 15537, 14162, 12787,
12600, 11400, 5500, 4512, 4502, 4502, 4500, 4500
]
}],
drilldown: {
series: []
}
}
}
}

Adding background color in d3 js bar chart using data values

I created a bar chart which was quite simple. Now, I want to add some style to the created bar chart. As shown in the example I want to add red color when x value is greater than 200. I tried various style fill and background but couldn't get the expected result. Any idea how to approach now?
Added the code
<script>
var mydata = {
"min": 68.9813,
"avg": 177.5037,
"max": 672.6713,
"values": [{
"bin": -50.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 0.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 50.0,
"percent": 6.7028,
"samples": 309
}, {
"bin": 100.0,
"percent": 32.2897,
"samples": 2407
}, {
"bin": 150.0,
"percent": 32.4565,
"samples": 3207
}, {
"bin": 200.0,
"percent": 17.1745,
"samples": 2064
}, {
"bin": 250.0,
"percent": 6.1833,
"samples": 940
}, {
"bin": 300.0,
"percent": 2.4971,
"samples": 444
}, {
"bin": 350.0,
"percent": 1.2438,
"samples": 279
}, {
"bin": 400.0,
"percent": 0.9262,
"samples": 182
}, {
"bin": 450.0,
"percent": 0.2781,
"samples": 71
}, {
"bin": 500.0,
"percent": 0.0962,
"samples": 24
}, {
"bin": 550.0,
"percent": 0.074,
"samples": 25
}, {
"bin": 600.0,
"percent": 0.0535,
"samples": 24
}, {
"bin": 650.0,
"percent": 0.0243,
"samples": 6
}, {
"bin": 700.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 750.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 800.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 850.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 900.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 950.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 1000.0,
"percent": 0.0,
"samples": 0
}],
"index": 7,
"time_h": 13.8529,
"stddev": 67.8836,
"samples": 9982
};
//set the dimensions and margins of the graph
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(2)
}
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(10)
}
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("#co_histogram").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
.style('fill', 'black');;
// get the data
// d3.csv("sales.csv", function(error, data) {
// if (error) throw error;
// // format the data
// data.forEach(function(d) {
// d.sales = +d.sales;
// });
// Scale the range of the data in the domains
x.domain(mydata.values.map(function(d) { return d.bin; }));
y.domain([0, d3.max(mydata.values, function(d) { return d.percent; })]);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(mydata.values)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.bin) + (x.bandwidth()-4) / 2; })
.attr("width", Math.min(x.bandwidth(),5))
.attr("y", function(d) { return y(d.percent); })
.attr("height", function(d) { return height - y(d.percent); });
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
);
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
);
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
</script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="../js/d3.v4.min.js"></script>
You can do that using a simple SVG rectangle appended before the black bars. For horizontally positioning the rectangle, just use your x scale:
var redBox = svg.append("rect")
.attr("x", x(200) + x.bandwidth() / 2)
.attr("y", 0)
.attr("width", x(x.domain()[x.domain().length - 1]) - x(200) + x.bandwidth() / 2)
.attr("height", height)
.attr("fill", "red")
.attr("opacity", 0.2);
Here is your code with that change:
var mydata = {
"min": 68.9813,
"avg": 177.5037,
"max": 672.6713,
"values": [{
"bin": -50.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 0.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 50.0,
"percent": 6.7028,
"samples": 309
}, {
"bin": 100.0,
"percent": 32.2897,
"samples": 2407
}, {
"bin": 150.0,
"percent": 32.4565,
"samples": 3207
}, {
"bin": 200.0,
"percent": 17.1745,
"samples": 2064
}, {
"bin": 250.0,
"percent": 6.1833,
"samples": 940
}, {
"bin": 300.0,
"percent": 2.4971,
"samples": 444
}, {
"bin": 350.0,
"percent": 1.2438,
"samples": 279
}, {
"bin": 400.0,
"percent": 0.9262,
"samples": 182
}, {
"bin": 450.0,
"percent": 0.2781,
"samples": 71
}, {
"bin": 500.0,
"percent": 0.0962,
"samples": 24
}, {
"bin": 550.0,
"percent": 0.074,
"samples": 25
}, {
"bin": 600.0,
"percent": 0.0535,
"samples": 24
}, {
"bin": 650.0,
"percent": 0.0243,
"samples": 6
}, {
"bin": 700.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 750.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 800.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 850.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 900.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 950.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 1000.0,
"percent": 0.0,
"samples": 0
}],
"index": 7,
"time_h": 13.8529,
"stddev": 67.8836,
"samples": 9982
};
//set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(2)
}
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(10)
}
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
.style('fill', 'black');;
// get the data
// d3.csv("sales.csv", function(error, data) {
// if (error) throw error;
// // format the data
// data.forEach(function(d) {
// d.sales = +d.sales;
// });
// Scale the range of the data in the domains
x.domain(mydata.values.map(function(d) {
return d.bin;
}));
y.domain([0, d3.max(mydata.values, function(d) {
return d.percent;
})]);
var redBox = svg.append("rect")
.attr("x", x(200) + x.bandwidth()/2)
.attr("y", 0)
.attr("width", x(x.domain()[x.domain().length - 1]) - x(200) + x.bandwidth() / 2)
.attr("height", height)
.attr("fill", "red")
.attr("opacity", 0.2);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(mydata.values)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.bin) + (x.bandwidth() - 4) / 2;
})
.attr("width", Math.min(x.bandwidth(), 5))
.attr("y", function(d) {
return y(d.percent);
})
.attr("height", function(d) {
return height - y(d.percent);
});
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
);
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
);
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
<script src="https://d3js.org/d3.v4.min.js"></script>

Extending the axis bar height in d3 js charts

I have created a bar chart using d3. Now I want extend the height of the x axis but couldn't find out how to do so. The height attr doesnt work and stroke attr just updates the tick text. Is there any way to extend height without changing the text labels.
Here is the link of what I am trying to do : Adding background color in d3 js bar chart using data values
As I suggested in your previous question, just add a rectangle before appending the axis:
var orangeBox = svg.append("rect")
.attr("x", 0)
.attr("y", height)
.attr("width", width)
.attr("height", margin.bottom)
.attr("fill", "orange")
.attr("opacity", 0.2);
Here is your code with that change:
var mydata = {
"min": 68.9813,
"avg": 177.5037,
"max": 672.6713,
"values": [{
"bin": -50.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 0.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 50.0,
"percent": 6.7028,
"samples": 309
}, {
"bin": 100.0,
"percent": 32.2897,
"samples": 2407
}, {
"bin": 150.0,
"percent": 32.4565,
"samples": 3207
}, {
"bin": 200.0,
"percent": 17.1745,
"samples": 2064
}, {
"bin": 250.0,
"percent": 6.1833,
"samples": 940
}, {
"bin": 300.0,
"percent": 2.4971,
"samples": 444
}, {
"bin": 350.0,
"percent": 1.2438,
"samples": 279
}, {
"bin": 400.0,
"percent": 0.9262,
"samples": 182
}, {
"bin": 450.0,
"percent": 0.2781,
"samples": 71
}, {
"bin": 500.0,
"percent": 0.0962,
"samples": 24
}, {
"bin": 550.0,
"percent": 0.074,
"samples": 25
}, {
"bin": 600.0,
"percent": 0.0535,
"samples": 24
}, {
"bin": 650.0,
"percent": 0.0243,
"samples": 6
}, {
"bin": 700.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 750.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 800.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 850.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 900.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 950.0,
"percent": 0.0,
"samples": 0
}, {
"bin": 1000.0,
"percent": 0.0,
"samples": 0
}],
"index": 7,
"time_h": 13.8529,
"stddev": 67.8836,
"samples": 9982
};
//set the dimensions and margins of the graph
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// set the ranges
var x = d3.scaleBand()
.range([0, width])
.padding(0.1);
var y = d3.scaleLinear()
.range([height, 0]);
function make_x_gridlines() {
return d3.axisBottom(x)
.ticks(2)
}
// gridlines in y axis function
function make_y_gridlines() {
return d3.axisLeft(y)
.ticks(10)
}
// append the svg object to the body of the page
// append a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")")
.style('fill', 'black');;
// get the data
// d3.csv("sales.csv", function(error, data) {
// if (error) throw error;
// // format the data
// data.forEach(function(d) {
// d.sales = +d.sales;
// });
// Scale the range of the data in the domains
x.domain(mydata.values.map(function(d) {
return d.bin;
}));
y.domain([0, d3.max(mydata.values, function(d) {
return d.percent;
})]);
var redBox = svg.append("rect")
.attr("x", x(200) + x.bandwidth() / 2)
.attr("y", 0)
.attr("width", x(x.domain()[x.domain().length - 1]) - x(200) + x.bandwidth() / 2)
.attr("height", height)
.attr("fill", "red")
.attr("opacity", 0.2);
var orangeBox = svg.append("rect")
.attr("x", 0)
.attr("y", height)
.attr("width", width)
.attr("height", margin.bottom)
.attr("fill", "orange")
.attr("opacity", 0.2);
// append the rectangles for the bar chart
svg.selectAll(".bar")
.data(mydata.values)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.bin) + (x.bandwidth() - 4) / 2;
})
.attr("width", Math.min(x.bandwidth(), 5))
.attr("y", function(d) {
return y(d.percent);
})
.attr("height", function(d) {
return height - y(d.percent);
});
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_gridlines()
.tickSize(-height)
.tickFormat("")
);
// add the Y gridlines
svg.append("g")
.attr("class", "grid")
.call(make_y_gridlines()
.tickSize(-width)
.tickFormat("")
);
// add the x Axis
svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// add the y Axis
svg.append("g")
.call(d3.axisLeft(y));
<script src="https://d3js.org/d3.v4.min.js"></script>

highcharts solid gauge: bigger tick marks

I am making a solid gauge with highcharts. I am trying to make the ticks long enough to span both data series. The goal is for a white line to cut through the colored bands at 10, 6, 2.9, and 0%. Notice how at 2.9%, the tick doesn't go all the way through (even though the length is set to 100).
I have tried yAxis.tickLength to make the ticks longer, to no avail. I also tried yAxis.tickPosition: 'inside'. That doesn't solve it either. Here is my JSFiddle.
http://jsfiddle.net/ncernek/wy6bo63p/2/
Thank you for your help.
If you add dummy second pane and yAxis, then ticks will be both inside and outside - because in fact there will be 2 ticks looking like one.
Example: http://jsfiddle.net/Lcz6juea/
And if I get wrong index of ticks (over series) then you could use this code without zIndex set for axes: http://jsfiddle.net/Lcz6juea/1/
$(function() {
// Uncomment to style it like Apple Watch
/*
if (!Highcharts.theme) {
Highcharts.setOptions({
chart: {
backgroundColor: 'black'
},
colors: ['#F62366', '#9DFF02', '#0CCDD6'],
title: {
style: {
color: 'silver'
}
},
tooltip: {
style: {
color: 'silver'
}
}
});
}
// */
Highcharts.setOptions({
chart: {
backgroundColor: 'white'
},
colors: ['#FE670A', '#0277a0', 'white']
});
Highcharts.chart('container', {
chart: {
type: 'solidgauge',
marginTop: 50
},
title: {
text: 'Discepant Reads',
style: {
fontSize: '24px'
}
},
tooltip: {
borderWidth: 0,
backgroundColor: 'none',
shadow: false,
style: {
fontSize: '16px'
},
pointFormat: '{series.name}<br><span style="font-size:2em; color: {point.color}; font-weight: bold; text-align: center">{point.y}%</span>',
positioner: function(labelWidth, labelHeight) {
return {
x: 200 - labelWidth / 2,
y: 180
};
}
},
pane: [{
startAngle: -140,
endAngle: 140,
background: [{ // Track for Move
outerRadius: '112%',
innerRadius: '88%',
backgroundColor: Highcharts.Color(Highcharts.getOptions().colors[0]).setOpacity(0.3).get(),
borderWidth: 0,
shape: 'arc'
}, { // Track for Exercise
outerRadius: '87%',
innerRadius: '63%',
backgroundColor: Highcharts.Color(Highcharts.getOptions().colors[1]).setOpacity(0.3).get(),
borderWidth: 0,
shape: 'arc'
}, { // Track for Stand
outerRadius: '62%',
innerRadius: '38%',
backgroundColor: Highcharts.Color(Highcharts.getOptions().colors[2]).setOpacity(0.3).get(),
borderWidth: 0,
shape: 'arc'
}]
}, {
startAngle: -140,
endAngle: 140,
size: '95%',
background: []
}],
yAxis: [{
reversed: true,
min: 0,
max: 10,
lineWidth: 0,
tickLength: 100,
tickWidth: 4,
tickColor: 'white',
tickPosition: 'outside',
minorTickLength: 0,
tickPositions: [0, 2.9, 6, 10],
zIndex: 4,
labels: {
distance: 30,
enabled: true,
x: 0,
y: 0,
format: '{value} %',
style: {
fontSize: 16
}
}
}, {
pane: 1,
linkedTo: 0,
reversed: true,
min: 0,
max: 10,
lineWidth: 0,
tickLength: 100,
tickWidth: 4,
tickColor: 'white',
tickPosition: 'inside',
minorTickLength: 0,
tickPositions: [0, 2.9, 6, 10],
zIndex: 4,
labels: {
enabled: false
}
}],
plotOptions: {
solidgauge: {
borderWidth: '34px',
dataLabels: {
enabled: false
},
linecap: 'round',
stickyTracking: false
}
},
series: [{
name: 'Your Score',
borderColor: Highcharts.getOptions().colors[0],
data: [{
color: Highcharts.getOptions().colors[0],
radius: '100%',
innerRadius: '100%',
y: 4
}],
dataLabels: {
borderRadius: 0,
backgroundColor: "#fff",
borderWidth: 0,
borderColor: "#FFF",
style: {
fontSize: "50px"
},
color: "grey",
crop: true,
formatter: function() {
var s;
s = '<span style="font-size: 50px;">' + this.point.y + '</span>';
return s;
},
y: -30,
zIndex: 90
}
}, {
name: 'Department Average',
borderColor: Highcharts.getOptions().colors[1],
data: [{
color: Highcharts.getOptions().colors[1],
radius: '75%',
innerRadius: '75%',
y: 6
}]
}, {
name: '',
borderColor: Highcharts.getOptions().colors[2],
data: [{
color: Highcharts.getOptions().colors[2],
radius: '50%',
innerRadius: '50%',
y: 50
}]
}]
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.1/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/solid-gauge.js"></script>
<div id="container" style="width: 400px; height: 400px; margin: 0 auto">
</div>
Using JavaScript
Highcharts.wrap(Highcharts.Tick.prototype, 'getMarkPath', function (prev, x, y, tickLength, tickWidth, horiz, renderer) {
return renderer.rect(x, y, 2, 80, 0)
.attr({
'stroke-width': 2,
stroke: 'white',
zIndex: 4,
style: 'transform: rotateZ(45deg) translateY(-290px)'
}).add();
});
You'd have to figure out a way to override each tick, but it's doable.
--
Using CSS
The (almost) pure CSS way is to target the exact SVG path and apply a transform to it. By no means is this way ideal, but it does provide a way to get the job done.
.highcharts-axis path:nth-child(2) {
transform: scale3d(1.6, 1, 1) rotateY(-59.7deg) translate(0, 25px);
stroke-width: 2;
}
.highcharts-axis path:nth-child(3) {
transform: scale3d(3.9, 1, 1) rotateY(-70deg) translate(0, 56px);
stroke-width: 2;
}
You would also have to adjust the zIndex attribute of the yAxis so that the redrawn ticks are on top of the other paths:
yAxis: {
...
zIndex: 4
}

Resources