I am trying to create a barchart (not rowchart) using d3.js and dc.js. I can create the bars ok, but they are all the same colour.
I have tried :
ordinalColors()
scaleOrdinal.range()
Renderlet
colorAccessor
but they all give the one colour for all bars, except colorAccessor which displays no bars at all.
I am using dc.js (version 3.0.2) and d3.js (version 5.4.0) and crossfilter (version 1.3.5).
Could someone please tell me an approach that will work to get different colours on each of my bars please?
Here is my code:
fruits = [{
"name": "apple",
"cnt": 10
},
{
"name": "orange",
"cnt": 15
},
{
"name": "banana",
"cnt": 12
},
{
"name": "grapefruit",
"cnt": 2
},
{
"name": "grapefruit",
"cnt": 4
},
{
"name": "pomegranate",
"cnt": 1
},
{
"name": "lime",
"cnt": 12
},
{
"name": "grape",
"cnt": 50
}
];
var ndx = crossfilter(fruits);
fruitDimension = ndx.dimension(function(d) {
return d.name;
}),
sumGroup = fruitDimension.group().reduceSum(function(d) {
return d.cnt;
});
fruitColours = d3.scaleOrdinal().range(["red", "yellow", "purple", "pink", "green", "orange", "blue"]);
barChart
// .ordinalColors(["red", "yellow", "purple", "pink", "green", "orange", "blue"])
// .ordinalColors(fruitColours)
// .ordinalColors(["#79CED7", "#66AFB2", "#C96A23", "#D3D1C5", "#F5821F"])
.width(768)
.height(380)
.x(d3.scaleBand())
.xUnits(dc.units.ordinal)
.brushOn(false)
.xAxisLabel('Fruit')
.yAxisLabel('Quantity Sold')
.dimension(fruitDimension)
.barPadding(0.1)
.outerPadding(0.05)
.group(sumGroup)
.colorAccessor(["#79CED7", "#66AFB2", "#C96A23", "#D3D1C5", "#F5821F", "grey", "purple"])
// .on("renderlet", function(chart){
// var colors =d3.scaleOrdinal().domain(["apple", "orange", "banana", "grapefruit", "pomegranate", "lime", "grape"])
// .range(["steelblue", "brown", "red", "green", "yellow", "grey", "purple"]);
// chart.selectAll('rect.bar').each(function(d){
//// d3.select(this).attr("style", "fill: " + colors(d.key)); // use key accessor if you are using a custom accessor
// d3.select(this).attr("style", "fill: " + colors(d.name)); // use key accessor if you are using a custom accessor
// });
// });
;
dc.renderAll();
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>School Donations Dashboard</title>
<link rel="stylesheet" href="vendor/css/bootstrap.min.css" />
<link rel="stylesheet" href="vendor/css/dc.css" />
<link rel="stylesheet" href="vendor/css/keen-dashboards.css" />
<link rel="stylesheet" href="css/custom.css" />
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="navbar-header">
<a class="navbar-brand" href="index.xhtml">Dashboard</a>
</div>
</div>
</div>
<div id="outer" class="container-fluid">
<!-- Test Bar Chart -->
<div class="chartWrapper">
<div class="chartTitle">
Bar Chart
</div>
<div class="chartStage">
<div id="barChart"></div>
</div>
</div>
<!-- Test Bar Chart -->
</div>
<!-- /outer -->
<hr />
<script src="vendor/js/jquery.min.js">
</script>
<script src="vendor" name="js/bootstrap.min.js">
</script>
<script src="vendor" name="js/crossfilter.js">
</script>
<script src="vendor/js/d3-v5.min.js">
</script>
<script src="vendor/js/d3-scale.min.js">
</script>
<script src="vendor/js/dc.js">
</script>
<script src="vendor/js/queue.js">
</script>
<script src="vendor/js/keen.min.js">
</script>
<script src="js/graph.js">
</script>
</body>
</html>
It looks like the problem you ran into is confusion between the role of the color accessor (.colorAccessor()) and the color scale (.colors()).
In all dc.js charts, an accessor function will be used to fetch the value that should determine the color. Then this value will be passed through a color scale in order to determine the actual color.
This is handy because for the most part your data is not going to have actual colors in it. The color scale takes some value from your data and maps it to a color.
When you specify the colorAccessor you need to give it a function which takes the data supplied by the group, in {key, value} form, and extracts the value you want mapped to a color.
If you want to change the colors used, you can use .colors(), supplying a d3 scale appropriate for your data, or you can use the convenience functions .ordinalColors() or .linearColors() and give the colors you want.
That's all you need for most charts. But for stacked charts (bar and line), you also need to specify the color accessor. This is because by default the stack mixin uses the layer (stack name) to key the color:
_chart.colorAccessor(function (d) {
var layer = this.layer || this.name || d.name || d.layer;
return layer;
});
(source)
So if you want unique colors for each bar, basing the color accessor on the group key is a good bet:
chart
.colorAccessor(d => d.key)
.ordinalColors(["#79CED7", "#66AFB2", "#C96A23", "#D3D1C5", "#F5821F", "grey", "purple"]);
However you'll need to make sure to specify at least as many ordinal colors as there are bars because an ordinal scale will repeat when the domain is larger size than the range.
Demo fiddle.
Related
In the kendo UI it is possible to load the grid with data from a variable in the Vue.js?
Yes, instead of using a data-source-ref, you can bind to a data-source property. This can be an instance of an kendo DataSource or a simple array.
For example, here's the default demo, changed to bind to an array of objects.
var products = [{
"ProductID": 1,
"ProductName": "Chai",
"UnitPrice": 18,
"UnitsInStock": 39,
"Discontinued": false
},
{
"ProductID": 2,
"ProductName": "Chang",
"UnitPrice": 19,
"UnitsInStock": 17,
"Discontinued": false
}, {
"ProductID": 3,
"ProductName": "Aniseed Syrup",
"UnitPrice": 10,
"UnitsInStock": 13,
"Discontinued": false
}
];
new Vue({
el: '#app',
data: {
products: products
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<title></title>
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.3.1026/styles/kendo.common.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.3.1026/styles/kendo.default.min.css" />
<link rel="stylesheet" href="https://kendo.cdn.telerik.com/2017.3.1026/styles/kendo.default.mobile.min.css" />
<script src="https://kendo.cdn.telerik.com/2017.3.1026/js/kendo.all.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.15/browser-polyfill.min.js"></script>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<script src="https://unpkg.com/#progress/kendo-all-vue-wrapper/dist/cdn/kendo-all-vue-wrapper.min.js"></script>
</head>
<body>
<div id="example">
<div id="app">
<kendo-grid :height="550" :data-source="products">
<kendo-grid-column field="ProductName"></kendo-grid-column>
<kendo-grid-column field="UnitPrice" title="Unit Price" :width="120" :format="'{0:c}'"></kendo-grid-column>
<kendo-grid-column field="UnitsInStock" title="Units In Stock" :width="120"></kendo-grid-column>
<kendo-grid-column field="Discontinued" :width="120"></kendo-grid-column>
</kendo-grid>
</div>
</div>
</body>
</html>
Yes, it is possible. In a grid action, try #Select and implement a function OnSelect (e). This is how you can get data from object e.
I have a time series with a date, an amount and a count column. I just want to plot the aggregate of amount by month and select a month by clicking on the bar, not using the brush.
I thought my objective was pretty simple, but I'm rummaging for days without success. The main issue is that I apply a filter on the chart, but the filter is not taken into account when a redraw the chart.
Thanks for your help.
I'm using :
dc.js 2.0.2
d3.js 3.5.17
crossfilter 1.4
This is my code :
<!DOCTYPE html>
<html lang="en">
<head>
<title>Just selecting a month </title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="../static/lib/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="../static/lib/css/dc.css"/>
</head>
<body>
<div>
Month selector
<a class="reset" href='javascript:chart.filterAll();dc.redrawAll();'> Reset</a>
<div id="time-chart"></div>
</div>
<script type="text/javascript" src="../static/lib/js/d3.js"></script>
<script type="text/javascript" src="../static/lib/js/crossfilter.js"></script>
<script type="text/javascript" src="../static/lib/js/dc.js"></script>
<script type="text/javascript">
var dateFormat_in = d3.time.format.utc("%Y-%m-%d");
var chart = dc.barChart("#time-chart");
d3.csv('setdates.csv', function(error, dataset) {
if(error)
throw new Error(error);
dataset.forEach(function(d) {
d["date"] = dateFormat_in.parse(d["date"]);
d["amount"] = +d["amount"];
});
var ndx = crossfilter(dataset);
var monthDim = ndx.dimension(d => d3.time.month(d["date"]));
var monthGroup = monthDim.group().reduceSum(d => d["amount"]);
var minDate = monthDim.bottom(1)[0]["date"];
var maxDate = monthDim.top(1)[0]["date"];
minDate=d3.time.day.offset(minDate, -40);
//console.log([minDate,maxDate]);
chart
.width(400)
.height(260)
.x(d3.time.scale().domain([minDate, maxDate]))
.xUnits(d3.time.months)
.dimension(monthDim)
.group(monthGroup)
.margins({left: 50, top: 20, right: 0, bottom: 20})
.elasticY(true)
.gap(60)
.centerBar(true).xAxisPadding(15).xAxisPaddingUnit('month')
.on('pretransition', function(ichart) {
ichart.selectAll("rect.bar").on("click", function (d) {
console.log([d.data.key,new Date(2016,d.data.key.getMonth()+1,1)]);
chart.filter([d.data.key,new Date(2016,5,1)]).redraw();
console.log(chart.filters())
//dc.renderAll();
});
})
.brushOn(false)
.clipPadding(20);
chart.centerBar(true).xAxisPadding(15).xAxisPaddingUnit('month')
dc.renderAll();
});
</script>
These are my data :
date,amount,count
2016-04-28,93.54,3.89
2016-04-29,94.42,3.94
2016-04-30,95.30,3.99
2016-05-02,97.06,4.08
2016-05-03,98.50,4.11
2016-05-04,99.94,4.13
2016-05-06,102.82,4.18
2016-05-07,104.26,4.20
2016-05-09,107.14,4.25
2016-05-10,109.27,4.26
2016-05-11,111.40,4.26
2016-05-12,113.53,4.27
2016-05-13,115.66,4.27
2016-05-14,117.78,4.28
2016-05-17,124.17,4.30
2016-05-18,126.30,4.30
2016-05-19,128.43,4.31
2016-05-20,130.56,4.32
2016-05-21,132.68,4.32
2016-05-23,136.94,4.33
2016-05-24,139.14,4.40
2016-05-25,141.35,4.48
2016-05-26,143.55,4.55
2016-05-27,145.75,4.62
2016-05-28,147.96,4.69
2016-05-30,152.36,4.83
2016-05-31,153.70,4.88
2016-06-01,155.04,4.93
2016-06-02,156.38,4.98
2016-06-03,157.73,5.02
2016-06-04,159.07,5.07
2016-06-06,161.75,5.17
2016-06-07,161.22,5.15
2016-06-08,160.70,5.14
2016-06-09,160.17,5.13
2016-06-10,159.64,5.12
2016-06-11,159.11,5.11
2016-06-13,158.06,5.08
2016-06-14,156.32,5.06
2016-06-15,154.59,5.04
2016-06-16,152.85,5.01
2016-06-17,151.12,4.99
2016-06-18,149.38,4.96
Interesting solution to this problem.
You probably want .redrawGroup() instead of .redraw() inside that handler, and you'll also need to wrap your range inside a dc.js filter object, specifically RangedFilter: unlike crossfilter's dimension.filter() dc.js's chart.filter() takes an object not an array.
Initial Range selection in DC.js chart
I am trying to show some marker on the static image ie
Given a static image of certain size in feet and set of point in feets how mark some image or a marker on the static image using openlayers3
I understand we have a provision in openlayer3 to use the static image as the base layer of the map
I am not getting how to show the marker on the static image(base layer)for given certain plots on the image
Any help would be more thank you please suggest a war to do it
I am show the static image as the map as shown below
<!DOCTYPE html>
<html>
<head>
<title>Static image example</title>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.css" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.js"></script>
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
</div>
<script>
// Map views always need a projection. Here we just want to map image
// coordinates directly to map coordinates, so we create a projection that uses
// the image extent in pixels.
var extent = [0, 0, 1024, 968];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
var map = new ol.Map({
layers: [
new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: 'colorful-triangles-background.jpg',
projection: projection,
imageExtent: extent
})
})
],
target: 'map',
view: new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2
})
});
</script>
</body>
</html>
But i have no idea how to plot the markers the plots are json given to plot is some thing like below
[{
x:1.234,
y:3.34,
units:feet
},
{
x:2.234,
y:4.34,
units:feet
},
{
x:7.234,
y:9.34,
units:feet
}]
Create an Icon Style
Create Icon Feature
Setup a New Vector layer with vector source
Add the vector layer in Map's layer
I have displayed the marker on click of the map at mouse position, you can add markers on the event you want
Also since I did not have the images you were referring I have referred the open layer examples image.
<!DOCTYPE html>
<html>
<head>
<title>Static image example</title>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com /bootstrap/3.3.4/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.css" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/ol3/3.6.0/ol.js"> </script>
</head>
<body>
<div class="container-fluid">
<div class="row-fluid">
<div class="span12">
<div id="map" class="map"></div>
</div>
</div>
</div>
<script>
// Map views always need a projection. Here we just want to map image
// coordinates directly to map coordinates, so we create a projection that uses
// the image extent in pixels.
var extent = [0, 0, 1024, 968];
var projection = new ol.proj.Projection({
code: 'xkcd-image',
units: 'pixels',
extent: extent
});
var iconStyle = new ol.style.Style({
image: new ol.style.Icon(({
anchor: [15, 24],
size: [32, 48],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
opacity: 0.75,
src: 'http://www2.psd100.com/ppp/2013/11/0501/Map-marker-icon-1105213652.png'
}))
});
//Create a Feature
var iconFeature = new ol.Feature({
geometry: new ol.geom.Point([72.5800, 23.0300])
});
//Setup a Vector Source
var vectorSource = new ol.source.Vector({
features: [iconFeature]
});
//Setup a Vector Layer
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
iconFeature.setStyle(iconStyle);
var map = new ol.Map({
layers: [
new ol.layer.Image({
source: new ol.source.ImageStatic({
attributions: [
new ol.Attribution({
html: '© xkcd'
})
],
url: 'http://imgs.xkcd.com/comics/online_communities.png',
projection: projection,
imageExtent: extent
})
}), vectorLayer //Add Vector in layers
],
target: 'map',
view: new ol.View({
projection: projection,
center: ol.extent.getCenter(extent),
zoom: 2
})
});
//On Map click setup marker
map.on('click', function (evt) {
var feature = new ol.Feature(new ol.geom.Point(evt.coordinate));
feature.setStyle(iconStyle);
vectorSource.clear();
vectorSource.addFeature(feature);
selectedlong = evt.coordinate[0];
selectedlat = evt.coordinate[1];
});
</script>
</body>
</html>
I'm making an html page with several examples of charts that I will be using. On the page I have a Dimple line graph, a pie chart, a wordcloud etc. When I try to add a second dimple graph - this time a bar graph, the first dimple line graph that I already have on the page is drawn on top of my bar graph:
My HTML file looks like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3 Graphs</title>
<link rel="stylesheet" type="text/css" href="_/base.css">
<link rel="stylesheet" type="text/css" href="_/c3CSS.css">
<script type="text/javascript" src="_/d3.min.js"></script>
<script type="text/javascript" src="_/dimple.v2.1.0.min.js"></script>
<script type="text/javascript" src="_/c3.min.js"></script>
<script type="text/javascript" src="_/d3.layout.cloud.js"></script>
</head>
<body>
<div id="chartContainer">
<h1>Hot Topics Line</h1>
<script type="text/javascript" src=CurvyLine.js></script>
</div>
<h1>Hot Topics Pie</h1>
<div id="chart">
<script type="text/javascript" src=Pie.js></script>
</div>
<div id="wordCloud">
<h1>Clickable Word Cloud</h1>
<script type="text/javascript" src=WordCloud.js></script>
</div>
<div id="bar">
<h1>Clickable Word Cloud</h1>
<script type="text/javascript" src=WeekBar.js></script>
</div>
</body>
</html>
Without adding the bar chart at the end, the line graph displays properly at the top of the page above the pie chart. However, with the bar chart added, both the line and bar graph are drawn inside the "bar" div. Can anyone help with this please? Here is my line graph js file:
var svg = dimple.newSvg("#chartContainer", 590, 400);
d3.tsv("data/tweet_example.tsv", function (data) {
//data = dimple.filterData(data, "Owner", ["Aperture", "Black Mesa"])
var myChart = new dimple.chart(svg, data);
myChart.setBounds(60, 30, 505, 305);
var x = myChart.addCategoryAxis("x", "Month");
x.addOrderRule("Date");
myChart.addMeasureAxis("y", "Tweets");
var s = myChart.addSeries("Topic", dimple.plot.line);
s.interpolation = "cardinal";
myChart.addLegend(60, 10, 500, 20, "right");
myChart.draw();
});
and here is my bar graph js file:
var svg = dimple.newSvg("#bar", 800, 410);
d3.tsv("data/tweet_example2.tsv", function (data) {
//data = dimple.filterData(data, "Owner", ["Aperture", "Black Mesa"])
var barChart = new dimple.chart(svg, data);
barChart.addCategoryAxis("x", ["Day", "Topic"]);
barChart.addMeasureAxis("y", "Tweets");
barChart.addSeries("Topic", dimple.plot.bar);
barChart.addLegend(65, 10, 510, 20, "right");
barChart.draw();
barChart.draw();
});
Your problem is that you are using the same global name svg to hold references to two different charts. When your second piece of code runs, it overwrites the svg value that you had from the first piece of code, and when the .tsv() callback returns, it finds a reference to the second graph.
Simplest solution: use different names for svg variable in both pieces of code: svg1 and svg2 will be fine.
Most elegant solution: use some kind of namespace management, such as wrapping both pieces of code in immediately called functions:
function() {
// your first chunk of code here
}()
function() {
// your second chunk of code here
}()
This way you will have two svg variables local to their own scopes
I am creating a line chart for a data series using C3.js.
I am struggling in trying to show the "points" only for one of the series.
Basically, first I am creating a multiple series line chart with some reference data, and then I am loading (with char.load) a new particular data line in which I want to show points, only for that particular line while the other reference lines remain with hidden points.
Is that possible via C3.js? If so, could you instruct me to do so, thanks!
Also, any method to do so using D3.js while using C3.js is welcome.
This is the official example in which all points are hidden for a data series, just for reference:
http://c3js.org/samples/point_show.html
c3.js provides comprehensive class attributes on all its elements so you can customize them with CSS. For example to hide the points on the 2nd series add this CSS:
#chart .c3-circles-data2 {
display: none;
}
Example here.
Here's an example of using the show and hide methods of a chart object to selectively display lines:
<!DOCTYPE html>
<html lang="en">
<head>
<title>show hide</title>
<meta charset="utf-8" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.css" rel="stylesheet" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.10/c3.min.js"></script>
</head>
<body>
<div id="chart"></div>
<script>
var chart = c3.generate({
bindto: '#chart',
data: {
x: 'x',
columns: [
['x', 1, 2, 3, 4, 5],
['y1', 3, 5, 6, 4, 5],
['y2', 2, 4, 7, 6, 5]
]
}
});
function cbclick(a){
var lineData = "y" + a;
var cbID = "cb" + a
var cb = document.getElementById(cbID);
if (cb.checked) {
chart.show([lineData]);
} else {
chart.hide([lineData]);
}
}
</script>
<div align="center">
<input type="checkbox" id="cb1" onclick="cbclick(1)" checked="true">y1</input>
<input type="checkbox" id="cb2" onclick="cbclick(2)" checked="true">y2</input>
</div>
</body>
There are a code for this purpose in the C3.js library:
point: {
show: false,
}
You can check this at here