Create offset for axis - X and Y should meet at 0, not Y-axis min value - c3.js

I am using c3.js to show temperature values, as a range I expect values from -30 to +50 degrees.
This works fine so far, but I am unhappy with the graphical representation.
I would like to have my X axis meet the Y axis at 0 and not at -30. Is this possible with c3.js? I already had a look at the manual and the examples, but I didn't find anything regarding offsetting the axis in this way.

You can hide default axis (1) and add custom line at zero (2).
See in action (jsfiddle)
var chart = c3.generate({
data: {
columns: [
['sample', 30, 20, -10, 40, 15, -25]
]
},
axis: {
x: {
show: false // (1)
},
y: {
max: 50,
min: -30,
// center: 0,
}
},
grid: {
y: {
lines: [{ value: 0, text: 'zero' }] // (2)
},
},
});
Related docs:
http://c3js.org/reference.html#axis-x-show
http://c3js.org/reference.html#grid-y-lines

Related

Threebox Tooltip in 3D models

I´ve been trying to enable tooltips on some imported 3D models, but it isnt working.
I already enabled tooltips in threbox, and I enabled tooltips in the options for the 3d element, as shown below.
tb = new Threebox(
map,
mbxContext,
{
realSunlight: true,
enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection
enableTooltips: true // change this to false to disable default tooltips on fill-extrusion and 3D models
}
);
var proptions = {
obj: './models/er.glb',
type: 'gltf',
scale: 10,
units: 'meters',
rotation: { x: 90, y: 0, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: 0, y: 0, z: 0.4 },
enableToltips: true
}
When i load the object i did the following:
tb.loadObj(proptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
tb.add(model);
});
Although the object appears in the render, when I put the mouse above or i click it nothing shows the tooltip.
What am I missing ?
EDIT:
Following #jscastro response i changed the import in the top of my html page to <link href="./threebox-plugin/examples/css/threebox.css" rel="stylesheet" /> (the path is the correct to where the file is)
I also removed the enableTooltip: true in proptions.
Despite that it still does not work, Below i will leave the code as it is:
var origin = [-8.4, 41.20, 1];
var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/streets-v11',
center: origin,
zoom: 11,
pitch: 30,
antialias: true
});
//Things related to dateTime ommited
window.tb = new Threebox(
map,
map.getCanvas().getContext('webgl'),
{
realSunlight: true,
enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection
enableTooltips: true // change this to false to disable default tooltips on fill-extrusion and 3D models
}
);
map.on('style.load', async function () {
await importarLinhas();
// stats
// stats = new Stats();
// map.getContainer().appendChild(stats.dom);
animate();
map.addLayer({
id: 'custom_layer',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, mbxContext) {
var eroptions = {
obj: './models/stationBus.fbx',
type: 'fbx',
scale: 0.01,
units: 'meters',
rotation: { x: 90, y: 20, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: -0.1, y: -0.1, z: 0.4 }
}
var poptions = {
obj: './models/Busstop.fbx',
type: 'fbx',
scale: 0.03,
units: 'meters',
rotation: { x: 90, y: 20, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: -0.1, y: -0.1, z: 0.1 }
}
var proptions = {
obj: './models/er.glb',
type: 'gltf',
scale: 2.7,
units: 'meters',
rotation: { x: 90, y: 0, z: 0 }, //default rotation
anchor: 'center',
adjustment: { x: 0, y: 0, z: 0.4 }
}
allNos.forEach((element) => { //For each one of a list that i fill first
//center of where the objects are
var place = [element.lng, element.lat, 0];
//cylinder as "base" for each one of the 3d Models
**//in here i cant do the Tooltip for the object**
const geometry = new THREE.CylinderGeometry(0.6, 0.6, 0.15, 32);
const material = new THREE.MeshLambertMaterial({ color: 0x5B5B5B });
const cylinder = new THREE.Mesh(geometry, material);
var baseOptions = {
obj: cylinder,
anchor: 'center',
adjustment: { x: 0, y: 0, z: -0.4 }
}
let base = tb.Object3D(baseOptions);
base.setCoords(place);
base.setRotation({ x: 90, y: 0, z: 0 })
//The text is just for the test
base.addTooltip("A radar in the middle of nowhere", true);
// base.castShadow = true;
window.tb.add(base);
//next i check what type of element it is
//it can only be one at the same time, so i use different models for each type
if (element.tipo === "p") {
window.tb.loadObj(poptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
if (element.tipo === "er") {
window.tb.loadObj(eroptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
if (element.tipo === "pr") {
window.tb.loadObj(proptions, function (model) {
model.setCoords(place);
model.addTooltip("A radar in the middle of nowhere", true);
model.setRotation({ x: 0, y: 0, z: Math.floor(Math.random() * 100) })
// model.castShadow = true;
window.tb.add(model);
});
}
});
},
render: function (gl, matrix) {
window.tb.setSunlight(date, origin.center);
window.tb.update();
}
})
map.addLayer(createCompositeLayer());
map.on('SelectedFeatureChange', onSelectedFeatureChange);
});
EDIT
I downloaded the page you shared in the chat, and I found many different issues and mistakes in your code.
1. You're using the wrong property to enable the selection of 3D objects, you use enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection, that is for Mapbox fill-extrusions features as said in the comment, but not for 3D models and objects, you have to use enableSelectingObjects: true. Only adding this, your problem with the tooltips on mouse over will be solved.
tb = new Threebox(
map,
mbxContext,
{
realSunlight: true,
enableSelectingObjects: true, //enable 3D models over/selection
enableTooltips: true // enable default tooltips on fill-extrusion and 3D models
}
);
But I have found other issues...
2. Your models scale initialization is too small, so you are hiding them below the big shapes you have created. The scale of your bus stop is scale: 0.01 and you define a place which is on the ground var place = [element.lng, element.lat, 0];, so it's hidden inside this CylinderGeometry
If you use scale: 1 you will see how your bus stops raises from the cylinder.
3. Same with the bus, you initialize them with scale: 1, which make them be hidden below the tubes and cylinders you have created. If you initialize them with scale: 10, and you elevate them 5 meters from the floor let truck = model.setCoords([lngB, latB, 4]); then you will see them raising.
4. Your models have a wrong initialization params mixing anchor and adjustment. anchor: center will center the pivotal center of your object properly, but then you apply negative values to x and y (which means decenter the object), and a z value that elevates the pivotal center adjustment: { x: -0.1, y: -0.1, z: 0.4 }. If you want your model on altitude use the 3rd coord in setCoords.
5. Your Cylinders and Tubes for the bus stops and bus lines are huge, and also they have the wrong init params, as you set them below the ground level -0.4 units adjustment: { x: 0, y: 0, z: -0.4 } (something supported by Mapbox but very bad resolved and producing weird effects. My recommendation would be to make them almost flat and at the ground level with no adjustment param. const geometry = new THREE.CylinderGeometry(0.6, 0.6, 0.01, 32);.
Summarizing, check all of these changes and let me know if it works.

All tickvalues not getting displayed on X axis

I am making a stacked multi chart bar graph like this one
http://nvd3.org/examples/multiBar.html
Till now I am able to push my values on Y- axis and X axis too but the problem I am facing is that the all the values are not getting displayed on the x axis but only 10 values are getting displayed . I am using nvD3 library in my angular code . and displaying date on x axis.
$scope.options1 = {
chart: {
type: 'multiBarChart',
height: 600,
margin: {
top: 20,
right: 20,
bottom: 200,
left: 45
},
clipEdge: false,
duration: 500,
stacked: true,
groupSpacing: 0.1,
useInteractiveGuideline: true,
showMaxMin: false,
xAxis: {
axisLabel: 'Timeline',
showMaxMin: false,
tickFormat: function(d) {
return d3.time.format('%d-%m-%y')(new Date(d))
},
xScale:d3.time.scale(),
rotateLabels: '-70'
},
yAxis: {
axisLabel: 'Pending Bills',
axisLabelDistance: -20,
groupSpacing: 0.1,
tickFormat: function(d) {
return d3.format(',f')(d);
}
}
}
};
generating ticking value array using this function
$scope.options1.chart.xAxis.tickValues = function() {
var xTick = _.map(data.data.data[0].values, function(value) {
return value.x;
});
xTick = _.sortBy(xTick, function(date){ return new Date(date); });
console.log(xTick);
return xTick;
}
the output of the console.log(xTick) is something like this which is all dates -
["2015-09-01", "2015-09-02", "2015-09-03", "2015-09-04", "2015-09-05",
"2015-09-06", "2015-09-07", "2015-09-08", "2015-09-09", "2015-09-10",
"2015-09-11", "2015-09-12", "2015-09-13", "2015-09-14", "2015-09-15",
"2015-09-16", "2015-09-17", "2015-09-18", "2015-09-19", "2015-09-20",
"2015-09-21", "2015-09-22", "2015-09-23", "2015-09-24", "2015-09-25",
"2015-09-26", "2015-09-27", "2015-09-28", "2015-09-29", "2015-09-30",
"2015-10-01", "2015-10-02", "2015-10-03", "2015-10-04", "2015-10-05",
"2015-10-06", "2015-10-07", "2015-10-08", "2015-10-09", "2015-10-10",
"2015-10-11", "2015-10-12", "2015-10-13", "2015-10-14", "2015-10-15",
"2015-10-16", "2015-10-17", "2015-10-18", "2015-10-19", "2015-10-20"]
as much I read about the it. all the dates should be get plotted on x axis but they are not
If you want to display all the ticks on X-Axis, you can add this option to your chart options :
"reduceXTicks": false,
For extended option page you can visit :
Angular NVD3 - MultiBarChart
Hope it helps.
chart: {
type: 'chartType',
xAxis: {
ticks:8
}
}
You can try "ticks" property if you want to display a specific number of ticks on the axis.

d3 - colour changed after calling function

I am new to d3 and I try to make a stacked/grouped histogram.
I define a global colour definition like this:
var c_gender = d3.scale.ordinal()
.domain(["missing", "present"])
.range(["#54278f", "#DADAEB"]);
and I use it to fill the rects (5 rects for 2 variable = 10 rects if grouped and 5 if stacked) in my histogram chart. All works fine but I noticed that the domain of the colour has been changed. I use this color definition in other charts so, the domain is not correct.
After using the colours to modify the style of my rects, more values are added to the domain of the colour like this:
var dataset = [
[
{ x: 0, y: missing_age_array.length },
{ x: 1, y: missing_gender_array.length },
{ x: 2, y: missing_weight_array.length },
{ x: 3, y: missing_height_array.length },
{ x: 4, y: missing_ethnicity_array.length }
],
[
{ x: 0, y: present_age_array.length },
{ x: 1, y: present_gender_array.length },
{ x: 2, y: present_weight_array.length },
{ x: 3, y: present_height_array.length },
{ x: 4, y: present_ethnicity_array.length }
]
];
data_stack = d3.layout.stack()(dataset);
...
console.log(c_gender.domain());
var layers = vis.selectAll("layer")
.data(data_stack)
.enter().append("g")
.style("fill", function(d, i) { return c_gender(i / (n - 1)) ; })
.attr("class", "layer");
console.log(c_gender.domain());
...
and the console.log result is:
["missing", "present"]
["missing", "present", 0, 1]
I am not sure this explanation is clear.. but can somebody help me to understand why the domain of the color is changed after calling a function?
TYIA
-monica
Have a look at the documentation for ordinal scales:
Given a value x in the input domain, returns the corresponding value in the output range.
If the range was specified explicitly [...] and the given value x is
not in the scale’s domain, then x is implicitly added to the domain;
By calling c_gender(i / (n - 1)), you implicitly add 0 and 1 as values in the domain.
You'll have to rework your scale to match the values you want to use : i / (n - 1) can't directly yield missing and present

nvd3 - how to draw a line over a scatterPlusLineChart, with specific cordinates

I have plotted a scatter bubble chart using model 'scatterPlusLineChart', and its working fine. But I need to draw a line with specific points. Please help if anyone knows.
var chart;
nv.addGraph(function() {
chart = nv.models.scatterPlusLineChart()
.showDistX(true)
.showDistY(true)
.transitionDuration(300)
.color(d3.scale.category10().range());
chart.xAxis.tickFormat(d3.format('.02f'))
chart.yAxis.tickFormat(d3.format('.02f'))
var graphData = [{key : 'Group1',
values : [{x:1, y:5, shape : 'circle'}, {x:4, y:2, shape : 'circle'}]
},
{key : 'Group2',
values : [{x:4, y:3, shape : 'circle'}, {x:1, y:6, shape : 'circle'}]
}
]
d3.select('#test1 svg')
.datum(nv.log(graphData))
.call(chart);
nv.utils.windowResize(chart.update);
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
return chart;
});
Above is the existing code and following is my requirement
Draw two lines
1) through (1,5) to (4,3)
2) through (4,2) to (1,6)
Use the slope and intercept groups arguments to specify the lines parameters.
For example :
var graphData = [{
key: 'Group1',
values: [{
x: 1,
y: 5
}, {
x: 4,
y: 2
}],
intercept: 6,
slope: -1
}, {
key : 'Group2',
values : [{
x: 4,
y: 3
}, {
x: 1,
y: 6
}],
intercept: 7,
slope: -1
}]
I let you use the appropriate maths to calculate the slope and intercept of the trend lines.

jqPlot Show Label for a dashed horizontal line

I want to put a label to the CanvasOverlay Horizontal line and show it in the graph. Haven't found any documentation related to it. But was not successful. Any pointer to fix this issue would be appreciated.
var line3 = [['02/01/2012 00:00:00', '02/01/2012 01:00:00'], ['02/02/2012 00:00:00', '02/01/2012 06:00:00'], ['02/03/2012 00:00:00', '02/01/2012 06:00:00'], ['02/04/2012 00:00:00', '02/01/2012 06:00:00']];
var plot2 = $.jqplot('chart1', [line3], {
title:'Mouse Cursor Tracking',
axes:{
xaxis:{
min:'2012-02-01',
max:'2012-02-10',
Label: 'Day',
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%b %#d'
},
tickInterval:'1 day'
},
yaxis:{
min:'2012-02-01 00:00:00',
max:'2012-02-01 24:00:00',
Label: 'Time',
renderer:$.jqplot.DateAxisRenderer,
tickOptions:{
formatString:'%H'
},
tickInterval:'2 hour'
}
},
highlighter: {
show: false
},
cursor: {
show: true,
tooltipLocation:'sw'
},
canvasOverlay: {
show: true,
objects: [
{horizontalLine: {
name: 'pebbles',
y: new $.jsDate( '2012-02-01 05:00:00').getTime(),
lineWidth: 3,
color: 'rgb(100, 55, 124)',
shadow: true,
lineCap: 'butt',
xOffset: 0
}},
{dashedHorizontalLine: {
name: 'bam-bam',
y: new $.jsDate( '2012-02-01 10:00:00').getTime(),
lineWidth: 4,
dashPattern: [8, 16],
lineCap: 'round',
xOffset: '25',
color: 'rgb(66, 98, 144)',
shadow: false
}}
]
}
});
I recently had this same problem and came up with a solution that seems to work pretty well. First of all, you'll need to create a new function so that you can pass in the plot object "plot2". You can then access the various properties of your axes to help calculate where jqplot is rendering your horizontal line.
function applyChartText(plot, text, lineValue) {
var maxVal = plot.axes.yaxis.max;
var minVal = plot.axes.yaxis.min;
var range = maxVal + Math.abs(minVal); // account for negative values
var titleHeight = plot.title.getHeight();
if (plot.title.text.indexOf("<br") > -1) { // account for line breaks in the title
titleHeight = titleHeight * 0.5; // half it
}
// you now need to calculate how many pixels make up each point in your y-axis
var pixelsPerPoint = (plot._height - titleHeight - plot.axes.xaxis.getHeight()) / range;
var valueHeight = ((maxVal - lineValue) * pixelsPerPoint) + 10;
// insert the label div as a child of the jqPlot parent
var title_selector = $(plot.target.selector).children('.jqplot-overlayCanvas-canvas');
$('<div class="jqplot-point-label " style="position:absolute; text-align:right;width:95%;top:' + valueHeight + 'px;">' + text + '</div>').insertAfter(title_selector);
}
You're essentially grabbing the size of your graph's div, then subtracting out the # of pixels that make up the graph's title and the text of the x-axis labels. Then you can calculate how many pixels make up each point in your y-axis. Then it's just a matter of seeing where your line fits within the range and applying your label accordingly. You may have to tweak it in a few places, but this should work pretty well.

Resources