I'm trying to create a Bokeh plot which allows me toggle the visibility of lines in the figure by clicking checkboxes.
I started trying to use a js callback to a checkbox group, but found unfortunately that js callbacks have not actually been implemented for the checkbox group.
Could anyone suggest another way to toggle line visibility in a bokeh plot. I'm guessing that modifying the alpha properties of the lines is the way to go.
Here is some simple sample code for creating my plot:
from bokeh.plotting import figure, show, output_notebook
from bokeh.io import vform
from bokeh.models.widgets import CheckboxGroup
output_notebook()
x = [1, 2, 3, 4, 5]
y1 = [3, 5, 6, 4, 5]
y2 = [2, 4, 7, 6, 5]
p = figure()
line1 = p.line(x, y1, alpha=1, color='blue', legend='blue line')
line2 = p.line(x, y2, alpha=1, color='red', legend='red line')
cbgroup = CheckboxGroup(labels=["toggle blue line","toggle red line"], active=[0,1])
show(vform(p,cbgroup))
I'm guessing that since there hasn't been any response to my question that there isn't any way to use checkboxes in bokeh plots for web pages.
In that case I've been looking at other possible web chart libraries other than bokeh and found a nice one called C3.js. It is implemented purely in javascript, but since bokeh already requires some knowledge of js this perhaps isn't a big deal for bokeh users. Moreover the C3.js syntax seems to be pretty nice and simple, even for someone like me who knows very little js.
For anyone checking on this question, I've provided the html/js page code below which creates the equivalent C3.js plot with checkboxes that I was trying to make above with bokeh:
<!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>
Related
I would like to give a bokeh visualization to a friend to let him explore data.
For simplicity, I intend to give him a .html file: all callbacks must be in JS.
But I have not found how to use CDSView in bokehjs api
Here is an example with different -not working- leads:
from bokeh.plotting import figure
from bokeh.io import output_file, save
from bokeh.layouts import row
from bokeh.models import ColumnDataSource, CDSView, GroupFilter
from bokeh.models import CheckboxGroup, CustomJS
template = """
{% block postamble %}
<script type="text/javascript" src="https://cdn.bokeh.org/bokeh/release/bokeh-api-2.4.2.min.js"></script>
{% endblock %}
"""
output_file(filename="Visu.html", title='Visualisation', mode='cdn')
# get data
source = ColumnDataSource(dict(
x=[1, 2, 3, 1, 2, 3],
y=[1, 4, 9, 1, 2, 3],
category=['square', 'square', 'square', 'lin', 'lin', 'lin']))
view = CDSView(source=source,
filters=[
GroupFilter(column_name='category', group="lin"),
])
# plot
plot = figure(title="Comparison")
plot.scatter(x="x", y="y", source=source, view=view)
# code for selection of data in js
labels = ["lin", "square"]
checkbox_group = CheckboxGroup(labels=labels, active=[0])
my_js_code = """
// clear array of glyphs (=remove curves)
plot.renderers.length = 0;
// draw selection
let selected = checkboxes.active.map(i=>checkboxes.labels[i]);
for(let indice in selected) {
let name = selected[indice];
// create new view
var my_view = new Bokeh.CDSView({
source: my_source,
filters: [new Bokeh.GroupFilter({column_name: 'category', group: name})]
});
// -- ADDING new curve
// Does not work : "plot.scatter not a function"
//plot.scatter({field: "x"}, {field: "y"}, {source:my_source, view: my_view});
// Work, but no CDSView
var new_glyph = new Bokeh.Scatter({
x: { field: "x" },
y: { field: "y" },
view: my_view // useless
});
plot.add_glyph(new_glyph, my_source); // no CDSView : draw whole source
// plot.add_glyph(new_glyph, my_source, my_view); // no error, but doesn't work : draw whole source
//plot.add_glyph(new_glyph, my_source, view=my_view); // ReferenceError: assignment to undeclared variable view
}
// update
plot.change.emit();
"""
checkbox_group.js_on_change("active", CustomJS(code=my_js_code, args=dict(my_source=source, plot=plot, checkboxes=checkbox_group)))
save(row(checkbox_group, plot), "visu.html", template=template)
How could we use CDSView in JS callback ?
Thanks for your suggestions !
We have a map application that is used to show counties in a state. In many cases local cities have their own jurisdictions, so when showing a county area we need to take the county shape and remove the cities that are separate.
We have been able to do this using the geocode function in the API to get the county shape and then grab the polygon for each city. From there we get the geometries and use pushInterior to create the cutouts.
On the whole, this works fairly well and even handles some pretty complicated overlapping areas. At a very close zoom we see the intricate borders perfectly, but when we zoom out the polygon starts to become unstable.
A clean cutout at the closest zoom, will begin to show random overlay shapes or connections as we zoom out. It almost looks like the polygon is adjusting to a lower resolution and eliminating some of the detail points to draw which results in large blobs or connections between points that fill in large areas based on random lines.
The odd part is that we have it drawing the shapes and lines. The lines are correct and follow the outlines of the shapes as intended but the lighter shape color is the part that is not displaying correctly.
Is there any way to force the precision or redraw of the polygon so the fill content is matching the outline?
Please try with the below code, which explains how we can draw polygon with Holes on the Map.
/**
* Adds a polygon to the map
*
* #param {H.Map} map A HERE Map instance within the application
*/
function addPolygonToMap(map) {
var geoPolygon = new H.geo.Polygon(
// define exterior shape
new H.geo.LineString([52, 13, 100, 48, 2, 100, 48, 16, 100, 52, 13, 100]),
[ // define interior geometries - holes
new H.geo.LineString([48.5, 4.5, 0, 49.5, 8, 0, 48.5, 9, 0]),
new H.geo.LineString([48.5, 15, 0, 50, 11, 0, 51, 13, 0])
]
);
map.addObject(
new H.map.Polygon(geoPolygon, {
style: {
fillColor: '#FFFFCC',
strokeColor: '#829',
lineWidth: 8
}
})
);
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Europe
var map = new H.Map(document.getElementById('map'),
defaultLayers.vector.normal.map,{
center: {lat:52, lng:5},
zoom: 5,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Add Polygon with holes to the map
addPolygonToMap(map);
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Polygon with Holes on the Map</title>
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
<link rel="stylesheet" type="text/css" href="demo.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<link rel="stylesheet" type="text/css" href="../template.css" />
<script type="text/javascript" src='../test-credentials.js'></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<script>window.ENV_VARIABLE = 'developer.here.com'</script><script src='../iframeheight.js'></script></head>
<body id="markers-on-the-map">
<div class="page-header">
<h1>Polygon with Holes on the Map</h1>
<p>Display a map highlighting a region or area</p>
</div>
<p>This example displays a polygon with two triangular holes in it covering part of Western Europe displayed on a moveable map.</p>
<div id="map"></div>
<h3>Code</h3>
<p>A polygon is created using the <code>H.map.Polygon</code> class, passing in an
<code>H.geo.Polygon</code> holding the vertices for the outline and interior parts of the polygon.
The look-and-feel of the polygon can be altered by adding the <code>style</code> parameter</p>
<script type="text/javascript" src='demo.js'></script>
</body>
</html>
For more details please refer the below link.
https://developer.here.com/documentation/examples/maps-js/geoshapes/polygon-with-holes-on-the-map
In my highcharts, I already have a legend at the bottom left of the graph but want to add a custom status indicator for the entire graph. It's going to be a colored oval with a colored dot and a status on it like in the picture.
Should I be trying to use a custom legend or something else? I don't need to hide/show abiliity of the legend so maybe I'm thinking a label is more appropriate but it seems like those only go in the graph. I want something on the top right of the graph. I'm looking through the API to see what else is available but haven't found one that suites my needs.
edit-
seems like I might have to do "chart.renderer.text" but I'm not sure how to convert and make it work in typescript http://jsfiddle.net/phpdeveloperrahul/HW7Rm/
function (chart) {
var point = chart.series[0].data[8],
text = chart.renderer.text(
'Max',
point.plotX + chart.plotLeft + 10,
point.plotY + chart.plotTop - 10
).attr({
zIndex: 5
}).add(),
box = text.getBBox();
chart.renderer.rect(box.x - 5, box.y - 5, box.width + 10, box.height + 10, 5)
.attr({
fill: '#FFFFEF',
stroke: 'gray',
'stroke-width': 1,
zIndex: 4
})
.add();
});
The best way to add a custom element inside the legend is to use the legend.labelFormatter.
events: {
render() {
let chart = this,
legendAttr = chart.legend.box.getBBox(),
padding = 5;
chart.plus = chart.renderer.text('| +', chart.legend.box.parentGroup.alignAttr.translateX + legendAttr.width + padding, chart.spacingBox.height + padding / 2).attr({
cursor: 'pointer',
}).css({
fontSize: 20
}).on(
"click",
function() {
alert('plus was clicked')
}
).add()
}
}
API References:
https://api.highcharts.com/highcharts/legend.labelFormatter,
Demo: https://jsfiddle.net/BlackLabel/yu7qm9jx/1/
I decided to just get rid of the title and add custom css to the top of it.
I can achieve the background particles using examples from three.js,
How i can achieve the N shape they have made using particles and the hover effect also
Thanks in advance for your answers :)
Achieving such an effect is not easy, but if you want a start, here is a simplified demo: http://jsfiddle.net/fezany2x/4/
The trick is to avoid refreshing the whole canvas. Instead, just erase your particles and repaint them again:
ctx.fillStyle = "#004";
boxes.forEach(
function(box) {
ctx.fillRect(box.x - 2, box.y - 2, 8, 8);
}
);
ctx.fillStyle = "white";
boxes.forEach(
function(box) {
ctx.fillRect(box.x, box.y, 4, 4);
}
);
I have a bar chart (jqplot). How do I make it fill out the entire screen regardless of screen resolution? Like this one: https://www.desmos.com/calculator . It automatically resizes the chart when the window is changed.
What worked for me was this (adapted from http://www.jqplot.com/deploy/dist/examples/area.html):
<!DOCTYPE html>
<html>
<head>
<title>Filled (Area) Charts</title>
<script class="include" type="text/javascript" src="../jquery.min.js"></script>
<script class="include" type="text/javascript" src="../jquery.jqplot.min.js"></script>
<script class="include" type="text/javascript" src="../plugins/jqplot.categoryAxisRenderer.min.js"></script>
<script language="javascript" type="text/javascript">
$(document).ready(function(){
var l2 = [11, 9, 5, 12, 14];
var l3 = [4, 8, 5, 3, 6];
var l4 = [12, 6, 13, 11, 2];
var plot = $.jqplot('chart1b',[l2, l3, l4],{
stackSeries: true,
showMarker: false,
seriesDefaults: {
fill: true
},
axes: {
xaxis: {
renderer: $.jqplot.CategoryAxisRenderer,
ticks: ["Mon", "Tue", "Wed", "Thr", "Fri"]
}
}
});
resizePlot(plot);
$(window).bind('resize', function(event, ui) {
resizePlot(plot);
});
});
function resizePlot($plot) {
$($plot.targetId).height($(window).height() * 0.75);
$plot.replot( { resetAxes: true } );
}
</script>
</head>
<body>
<div class="example-content">
<div id="chart1b"></div>
</div>
</body>
</html>
The key is to bind a handler to the resize event of the window. With this you can get the size of the window and in turn modify the height of the plot container. We need to modify the height this way because the resize event only seemed to affect the width of the plot.
My example here sets the plot to be 75% of the height of the window - you will need to adjust this to suit your requirements.