Related
I have a fairly simple ant column chart, this is it:
import React from "react";
import ReactDOM from "react-dom";
import { Column } from "#ant-design/plots";
const DemoColumn = () => {
const data = [
{
type: "Value 1",
value: 315801
},
{
type: "Value 2",
value: 222095
},
{
type: "Value 3",
value: 10800
}
];
const config = {
data,
loading: data.length === 0,
xField: "type",
yField: "value",
seriesField: "type",
yAxis: {
label: {
formatter: (v) => v
}
},
xAxis: false,
height: 200,
autoFit: false,
legend: {
position: "bottom",
flipPage: false
},
interactions: [
{
type: "element-active"
}
],
label: {
position: "top",
offsetY: 8,
formatter: ({ value }) => value
}
};
return <Column {...config} />;
};
ReactDOM.render(<DemoColumn />, document.getElementById("container"));
The problem is, that the number of Value 1 is hanging off the chart. I tried setting the padding but this did not help, it acutally screwed the whole chart up?!?
Here is also a fiddle: https://codesandbox.io/s/cool-microservice-gbrbm9?file=/index.js:0-929
Fixed it myself by adding
appendPadding: 10
There is an Open Bug in AntD GitHub [BUG] Label of column charts are being cut out if label position is set to 'top' officials for this issue. Some member of ant design team has given an answer as set limitInPlot: true in config. I tried that but didn't work.
I tried with adjusting height prop and offsetY in lable prop inside config. This is just tricky option for you to achieve what you want.
Option 1 -
label: {
position: "top",
offsetY: 15, // change offset to this then value will not crop as before but just overlap with chart.
formatter: ({ value }) => value
}
Option 2 -
xAxis: false,
height: 750, // increase the chart height and it will show the value with a gap. But this will create a scroll.
autoFit: false,
Option 3 -
You can use mix of above two options with adjusting right values. This will get what you want without a scroll. below values got this result.
xAxis: false,
height: 500, // adjust height
autoFit: false,
legend: {
position: "bottom",
flipPage: false
},
interactions: [
{
type: "element-active"
}
],
label: {
position: "top",
offsetY: 12, // adjust offset
formatter: ({ value }) => value
}
This is the sandboxcode.
Hope this will help to overcome your issue.
I want to highlight fill-extrusion features when hovered over them.
The styling related to this is straight-forward using expressions and feature state, but I am having trouble retrieving the correct features.
There is code available online to change the feature state when hovered over, and it seems straight-forward enough, so I adapted it:
var hover_id = null;
const feature_state = { hover: true }
map.on('mousemove', '3d-buildings', (e) => {
// Get features under cursor, following render order
const features = map.queryRenderedFeatures(e.point);
// Check that features are not empty
if (features.length > 0) {
// Clean up previously hovered feature
if (hover_id) {
map.removeFeatureState({source: "composite", sourceLayer: 'building', id: hover_id});
}
// Set feature state of the new hovered feature
hover_id = features[0].id;
map.setFeatureState({source: 'composite', sourceLayer: 'building', id: hover_id}, feature_state);
console.log(hover_id)
}
});
While this works well initially, it stops working as soon as I tilt the camera using the right mouse button. After tilting, the foremost element no longer gets selected (something else seems to get selected and an ID gets printed out, but nothing shows up on the map and no error is thrown).
On a related note, the correct feature only gets selected after zooming in quite far - there is a large zoom range where the buildings already get rendered to the screen, but seem to not get picked up by queryRenderedFeatures. Is this expected behaviour?
Expected behaviour:
map.queryRenderedFeatures(...)[0] selects the foremost feature, independent of the camera tilt.
What could be a possible reason for the camera tilt influencing the feature selection? Is this a bug or am I misusing the API?
I think the issue you’re facing has nothing to do with tilt but with the fact that you’re adding and removing the state instead of changing the value of the state. The state must be declared in the layer definition, change the color with a expression, and then you only need to change the value of the state.
Here you have a fiddle I have created to show how to change color of fill extrusions on mouse over/out
Relevant code is this:
let mapConfig = {
NYC: {
origin: [-74.044514, 40.689259, 39],
center: [-74.0137, 40.70346, 0],
zoom: 16.2,
pitch: 60,
bearing: 35
}
}
mapboxgl.accessToken = 'PUT YOUR TOKEN HERE';
let point = mapConfig.NYC;
var map = new mapboxgl.Map({
style: 'mapbox://styles/mapbox/streets-v11',
center: point.center,
zoom: point.zoom,
pitch: point.pitch,
bearing: point.bearing,
container: 'map',
antialias: true,
hash: true
});
map.on('style.load', function() {
if (map.getSource('composite')) {
map.addLayer({
'id': '3d-buildings',
'source': 'composite',
'source-layer': 'building',
'type': 'fill-extrusion',
'minzoom': 14,
'paint': {
'fill-extrusion-color': [
'case',
['boolean', ['feature-state', 'hover'], false],
'#ff0000',
'#ddd'
],
'fill-extrusion-height': ["number", ["get", "height"], 5],
'fill-extrusion-base': ["number", ["get", "min_height"], 0],
'fill-extrusion-opacity': 1
}
}, 'road-label');
}
let fHover;
map.on('mousemove', function(e) {
//157001066
var features = map.queryRenderedFeatures(e.point, {
layers: ['3d-buildings']
});
if (features[0]) {
mouseout();
mouseover(features[0]);
} else {
mouseout();
}
});
map.on('mouseout', function(e) {
mouseout();
});
function mouseout() {
if (!fHover) return;
map.getCanvasContainer().style.cursor = 'default';
map.setFeatureState({
source: fHover.source,
sourceLayer: fHover.sourceLayer,
id: fHover.id
}, {
hover: false
});
}
function mouseover(feature) {
fHover = feature;
map.getCanvasContainer().style.cursor = 'pointer';
map.setFeatureState({
source: fHover.source,
sourceLayer: fHover.sourceLayer,
id: fHover.id
}, {
hover: true
});
}
});
If this answer solves your question, please mark it as answer accepted in that way it will also help other users to know it was the right solution.
#jscastro This works fine: My requirement is I need to change the color of few buildings with lat and lng. I have achieved getting buildings id's from lat and lng by using below API
https://api.mapbox.com/v4/mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2/tilequery/55.26365875255766,25.188400365955193.json?radius=30&limit=10&dedupe&access_token=.
I am facing one issue here, The colors are changing only after zoom level 17. I want to change the color on zoom level 15.
map.on("style.load", function () {
if (map.getSource("composite")) {
const layers = map.getStyle().layers;
const labelLayerId = layers.find(
(layer) => layer.type === "symbol" && layer.layout["text-field"]
).id;
map.addLayer(
{
id: "3d-buildings",
source: "composite",
"source-layer": "building",
filter: ["==", "extrude", "true"],
type: "fill-extrusion",
minzoom: 15,
zoom: 15,
pitch: 60,
bearing: -60,
layout: {
// Make the layer visible by default.
visibility: "visible",
},
paint: {
"fill-extrusion-color": [
"case",
["boolean", ["feature-state", "hover"], false],
"#00ff00",
"#AED0EC",
],
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.5,
["get", "height"],
],
"fill-extrusion-base": [
"interpolate",
["linear"],
["zoom"],
15,
0,
15.05,
["get", "min_height"],
],
"fill-extrusion-opacity": 1,
},
},
labelLayerId
);
}
map.getCanvasContainer().style.cursor = "pointer";
map.setFeatureState(
{
source: "composite",
sourceLayer: "building",
id: "4411722601841895",
},
{
hover: true,
}
);
map.getCanvasContainer().style.cursor = "pointer";
map.setFeatureState(
{
source: "composite",
sourceLayer: "building",
id: "1315660041727095",
},
{
hover: true,
}
);
map.getCanvasContainer().style.cursor = "pointer";
map.setFeatureState(
{
source: "composite",
minzoom: 15,
sourceLayer: "building",
id: "3957345234349675",
},
{
hover: true,
}
);
map.getCanvasContainer().style.cursor = "pointer";
map.setFeatureState(
{
source: "composite",
sourceLayer: "building",
id: "5328485811",
},
{
hover: true,
}
);
});
I'm trying to make a a combination of a candlestick chart (representing stock data) and a bar chart (representing volume).
I already have them displayed on one chart but the display and layout I'm having trouble with.
For one, the candlestick and bar data are placed side-by-side rather than stacked on top of each other. Another error is the scale of the volume data for the bar chart is not represented properly in the y-axis (which uses data from candlesticks as basis).
Here is my current code to render the chart:
chart = new Chart(ctx, {
type: 'candlestick',
data: {
labels: labelsData,
datasets: [{
label: "My Data",
data: chartData
},
{
label: 'Volume',
data: volData,
type: 'bar'
}]
}
});
labelsData contains the Date values for each item entry
chartData contains JSON object with c,h,l,o,t (close,high,low,open,date) to
represent stock data for each item entry
volData is an array containing numbers to represent volume for each item entry
What should I add to make the candlesticks and bars placed on the same column, as well as have the bars have their own scale so they do not overshoot the height of the chart?
It seems you need to scale the volume data since it's two different value units in Y,
It seems like currentlty there isn't support for this in chartJs I created a feature request, follow the link to see the two issues that were closed due to this.
https://github.com/apexcharts/apexcharts.js/issues/2068
With default configuration you're not easily able to add barcharts.
Here is steps you need to do;
Base config:
const config = {
// type: 'candlestick', // you must remove this, this option is braking the chart
data: {
datasets: []
},
options: {
parsing: false, // must be here, solves another stupid problem
spanGaps: true, // for better performance
animation: false, // for better performance
pointRadius: 0, // for better performance
plugins: {
title: {
display: false,
text: 'Fiyat Grafiği'
},
},
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
type: 'timeseries',
},
y: {
type: 'linear',
},
volume: {
type: 'linear',
beginAtZero: true,
position: 'right',
max: maxVolume * 10, // maxVolume should be the maximum number of volumes
grid: {
display: false, // for better presentation
},
ticks: {
display: false, // for better presentation
},
}
},
interaction: {
intersect: false,
mode: 'index',
},
}
};
Second step is preparing the datasets;
let dataSets = [
{
type: 'candlestick', // this must stay
label: 'Financial Graph',
data: data['klines'].map(function (kline) {
return {
'x': moment(kline['from']),
'o': kline['open_price'],
'c': kline['close_price'],
'h': kline['high_price'],
'l': kline['low_price']
};
}),
color: {
up: 'rgb(26, 152, 129)', // those colors are better than defaults
down: 'rgb(239, 57, 74)', // those colors are better than defaults
unchanged: '#999', // those colors are better than defaults
},
borderColor: {
up: 'rgb(26, 152, 129)',
down: 'rgb(239, 57, 74)',
unchanged: '#999',
},
order: 10,
yAxisID: 'y', // this must stay
},
{
type: 'bar',
label: 'Volume',
data: data['klines'].map(function (kline) {
return {
'x': moment(kline['from']), // i used moment, feel free to use your own time library
'y': kline.quote_asset_volume,
}
}),
backgroundColor: data['klines'].map(function (kline) {
return kline.open_price < kline.close_price ? 'rgb(26, 152, 129)' : 'rgb(239, 57, 74)' // for better presentation
}),
borderColor: '#fff',
borderWidth: 1,
order: 12,
yAxisID: 'volume', // this must stay
barPercentage: 0.5, // this must stay
barThickness: 6, // this must stay
maxBarThickness: 8, // this must stay
},
]
Result;
So I have a chart.js chart with two series. One is a bar chart and the other is a line graph.
S1 = 71,166,2,6,8
S2 = 6,2,4,8,5
When I plot them, they both appear on their own scales which makes the bar and line graphs kinda pointless.
I need a way to plot both charts on the same scale.
Is there a way to do this within chart.js? If not, how would you do it?
thanks.
var barChartData = {
labels: dataLabels,
datasets: [{
type: 'bar',
label: "Actual",
data: dataActual,
fill: false,
backgroundColor: '#71B37C',
borderColor: '#71B37C',
hoverBackgroundColor: '#71B37C',
hoverBorderColor: '#71B37C',
yAxisID: 'y-axis-2'
}, {
label: "Maximum",
type:'line',
data: dataMaximum,
fill: false,
borderColor: '#EC932F',
backgroundColor: '#EC932F',
pointBorderColor: '#EC932F',
pointBackgroundColor: '#EC932F',
pointHoverBackgroundColor: '#EC932F',
pointHoverBorderColor: '#EC932F',
yAxisID: 'y-axis-1'
} ]
};
$(function () {
if (theChart !== undefined) {
theChart.destroy();
}
var ctxActualVsMax = document.getElementById("myChart2").getContext("2d");
theChart = new Chart(ctxActualVsMax, {
type: 'bar',
data: barChartData,
options: {
responsive: true,
tooltips: {
mode: 'label'
},
elements: {
line: {
fill: false
}
},
scales: {
xAxes: [{
display: true,
gridLines: {
display: false
},
labels: {
show: true,
}
}],
yAxes: [{
type: "linear",
display: true,
position: "left",
id: "y-axis-1",
gridLines:{
display: false
},
labels: {
show:true,
}
}, {
type: "linear",
display: false,
position: "right",
id: "y-axis-2",
gridLines:{
display: false
},
labels: {
show:true,
}
}]
}
}
});
});
In your code you have specified your two datasets to use different yAxisId's. Just set them both to the same, you can also remove the unused yAxes object from the options
fiddle
Is there a way funnel chart will redraw like pie chart does, when a segment is removed?
Similar to this question redraw pie chart in highchart
http://jsfiddle.net/2Me2z/
$(function () {
var chart = new Highcharts.Chart({
chart: {
renderTo: 'container',
type: 'pie'
},
plotOptions: {
pie: {
showInLegend: true
}
},
legend: {
enabled: true
},
series: [{
data: [20, 30, 30, 20]
}]
});
// button handler
$('#button').click(function() {
var series = chart.series[0];
if (series.data.length) {
chart.series[0].data[0].remove();
}
});
});
So click on any slice in legend will cause the chart to redraw and the remaining slices will take up 100%
Wonder if the same thing can be done for a funnel chart
http://jsfiddle.net/YUL5c/2/
$(function () {
var chart;
$(document).ready(function () {
// Build the chart
$('#container').highcharts({
chart: {
type: 'funnel',
plotBackgroundColor: null,
plotBorderWidth: null,
plotShadow: false
},
title: {
text: 'Browser market shares at a specific website, 2010'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
plotOptions: {
series: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: false
},
showInLegend: true
}
},
series: [{
name: 'Browser share',
data: [
['Firefox', 45.0],
['IE', 26.8],
{
name: 'Chrome',
y: 12.8,
sliced: true,
selected: true
},
['Safari', 8.5],
['Opera', 6.2],
['Others', 0.01]
]
}]
});
});
});
Currently the segment just disaapear. But the chart does not redraw
Unfortunately this animation is not supported, but I advice to post your request on the uservoice website
point.visible flag is ignored in funnel code.
If adding the check back to the drawing logic things just work magically. Even for the animation.
Not sure if it is a bug or ignored by intention