What is the best way to create candlestick chart in plottable? I would like to do something like this image on wiki:
Wiki example
I was thinking about using stacked bar plot and styling it with css. For example lowermost value in bar would be transparent, just to position candlestick vertically. Next value in a bar would be lower stick(rectangle horizontally squeezed with css to become line). Next would be the body which is already rectangle and upper stick would again be rectangle squeezed to line.
Is this proper way to go or are there any more elegant solutions? Are there any examples out there? Has anyone done something like this before?
Glad you asked! The best way to do this in Plottable is to combine Plots.Rectangle and Plots.Segment using a Group. Here's an example:
window.onload = function() {
var xScale = new Plottable.Scales.Time();
var yScale = new Plottable.Scales.Linear();
var dataset = new Plottable.Dataset(exampleData);
var wicks = new Plottable.Plots.Segment();
wicks.addDataset(dataset);
wicks.x(function(d) { return parseDate(d.date, 12); }, xScale);
wicks.y(function(d) { return d.high; }, yScale);
wicks.y2(function(d) { return d.low; });
wicks.attr("stroke", "black");
var candles = new Plottable.Plots.Rectangle();
candles.addDataset(dataset);
candles.x(function(d) { return parseDate(d.date, 2); }, xScale);
candles.x2(function(d) { return parseDate(d.date, 22); });
candles.y(function(d) { return d.open; }, yScale);
candles.y2(function(d) { return d.close; });
candles.attr("fill", function(d) {
if (d.close > d.open) {
return "#63c261";
} else {
return "#fd373e";
}
});
var candlesticks = new Plottable.Components.Group([wicks, candles]);
candlesticks.renderTo("#chart");
};
function parseDate(dateString, hourOfDay) {
var day = new Date(dateString);
day.setHours(hourOfDay);
return day;
}
var exampleData = [
{
date: "2014-08-29",
open: 102.86,
high: 102.90,
low: 102.20,
close: 102.50
},
{
date: "2014-08-28",
open: 101.59,
high: 102.78,
low: 101.56,
close: 102.25
},
{
date: "2014-08-27",
open: 101.02,
high: 102.57,
low: 100.70,
close: 102.13
},
];
body { background-color: #AAA; }
svg { background-color: #FFF; }
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.16.2/plottable.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/plottable.js/1.16.2/plottable.css">
</head>
<body>
<svg id="chart" width="400" height="300"></svg>
</body>
</html>
Plots.Segment draws line segments, so setting the ends of each segment to the high/low on each day gets the "wicks" for each candlestick. Meanwhile, Plots.Rectangle draws rectangles, so we set the top/bottom of each rectangle to the open/close on each day. Overlaying the two Plots gets us a candlestick chart.
Hope this helps!
Related
In a webpage I load data from a csv file that contains like (it can contains months of data) :
timestamp,open,high,low,close
2022-08-03,1.01554,1.02105,1.01210,1.01618
2022-08-02,1.02578,1.02939,1.01619,1.01625
2022-08-01,1.02182,1.02753,1.02040,1.02587
2022-07-29,1.01952,1.02544,1.01440,1.02248
2022-07-28,1.02005,1.02344,1.01120,1.01947
2022-07-27,1.01174,1.02209,1.00950,1.01998
2022-07-26,1.02210,1.02502,1.01060,1.01179
2022-07-25,1.02174,1.02579,1.01770,1.02200
The first column is a date, but I think the google chart treat it like a string while creating the chart.
This is the code in html page I use to load data from csv and to create the chart:
<!DOCTYPE html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="jquery.csv-0.71.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script>
// load the visualization library from Google and set a listener
google.load("visualization", "1", {packages:["corechart"]});
google.setOnLoadCallback(drawChart);
</script>
<script>
function drawVisualization() {
$.get("EURUSD.csv", function(csvString) {
// transform the CSV string into a 2-dimensional array
var arrayData = $.csv.toArrays(csvString, {onParseValue: $.csv.hooks.castToScalar});
// this new DataTable object holds all the data
var data = new google.visualization.arrayToDataTable(arrayData);
var view = new google.visualization.DataView(data);
//view.setColumns([0,1]);
var options = {
legend: 'none',
title: 'EURUSD',
bar: { groupWidth: '100%' }, // Remove space between bars.
candlestick: {
fallingColor: { strokeWidth: 0, fill: '#a52714' }, // red
risingColor: { strokeWidth: 0, fill: '#0f9d58' } // green
}
};
var chart = new google.visualization.CandlestickChart(document.getElementById('chart_div'));
data.sort({column: 0, asc: true});
chart.draw(data, options);
});
}
google.setOnLoadCallback(drawVisualization)
</script>
<div id="chart_div" style="width: 900px; height: 500px;"></div>
enter code here
The chart I get is:
I would like to group by month or by year in the X asses, insted of everyday date printed there.
How can I do?
Thank You
Carlo
after you load the csv data...
var arrayData = $.csv.toArrays(csvString, {onParseValue: $.csv.hooks.castToScalar});
convert the first column to a date...
arrayData = arrayData.map(function (row) {
row[0] = new Date(row[0]);
return row;
});
In my previous post 'Leaflet JS - changing esri shape into marker on certain zoom level
' I was able to resolve an issue which i had with the leaflet JS library and changing the polygon shapes to markers icons when hitting a certain zoom level.
I was advised by 'Ivan Sanchez' to use the 'Leaflet.Deflate' plugin and this works like a charm, so now the various shapes are being transformed into markers after a certain zoomlevel, however now I'm struggling to change the default leaflet marker icon to a custom marker icon, so my question now is:
Is it possible to change the default marker icon to a custom marker icon while using the 'Leaflet.ShapeFile' and 'Leaflet.Deflate' plugin and what would be the best approach to do this?
I wanted to make a JSFiddle, but I don't JSFiddle allows me to attach the zip file contains the shapefiles, so I will post the code I have got so far below here, thanks for your help, advise and support:
<!doctype html>
<html lang="en">
<head>
<meta charset='utf-8' />
<title>v4</title>
<link rel="stylesheet" type="text/css" href="lib/leaflet/leaflet.css" />
<!--[if lte IE 8]> <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.ie.css" /> <![endif]-->
<link rel="stylesheet" type="text/css" href="lib/leaflet/L.Control.Sidebar.css" />
<style>
html { height: 100% }
body { height: 100%; margin: 0; padding: 0; }
#map { height: 100% }
</style>
</head>
<body>
<div id="map"></div>
<script src="lib/jquery/jquery-3.1.1.min.js"></script>
<script src="lib/leaflet/leaflet.js"></script>
<script src="lib/leaflet/catiline.js"></script>
<script src="lib/leaflet/leaflet.shpfile.js"></script>
<script src="lib/leaflet/shp.js"></script>
<script src="lib/leaflet/L.Control.Sidebar.js"></script>
<script src="lib/leaflet/L.Deflate.js"></script>
<script>
// init map
var m = L.map('map').setView([52.472833, 1.749609], 15);
// clicking on the map will hide the sidebar plugin.
m.on('click', function () {
sidebar.hide();
});
// init Deflate plugin
L.Deflate({ minSize: 10 }).addTo(m);
// Init side bar control
var sidebar = L.control.sidebar('sidebar', { closeButton: true, position: 'right' });
m.addControl(sidebar);
// Init esri shape file via leaflet.shapefile, shp.js plugin
var businessProperties = new L.Shapefile('data/businessshapes.zip', { style: propertyStyle, onEachFeature: propertyOnEachFeature }).addTo(m);
// create on-click Feature
function propertyOnEachFeature(feature, layer) {
layer.on( {
mouseover: highlightFeature,
mouseout: resetHighlight,
click: function populate() {
sidebar.toggle();
document.getElementById('pinfoHeader').innerHTML = "<h2>" + feature.properties.Building + " - Detailed Information</h2><br />";
document.getElementById('pTitle').innerHTML = "Name: " + feature.properties.Building
document.getElementById('pDetails').innerHTML = "SHAPE_Leng: " + feature.properties.SHAPE_Leng + "<br/ >SHAPE_Area: " + feature.properties.SHAPE_Area
}, highlightFeature, zoomToFeature
});
}
// style the properties style
function propertyStyle(feature) {
return {
fillColor: getPropertyColor(feature.properties.BusType),
weight: 2,
opacity: 1,
color: 'white',
dashArray: 3,
fillOpacity: 0.7
}
}
// set color per property according to the data table of the Esri Shape file.
function getPropertyColor(propStatus) {
if (propStatus == 'TypeA') {
return 'red';
} else if (propStatus == 'TypeB') {
return 'green';
} else {
return 'yellow';
}
}
// set the highlighted color for polygon
function highlightFeature(e) {
var layer = e.target;
layer.setStyle( {
weight: 2,
color: 'black',
fillColor: 'white',
fillOpacity: 0.2
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
}
// reset the highlighted color for polygon after mouse leave polygon
function resetHighlight(e) {
businessProperties.resetStyle(e.target);
}
//Extend the Default marker class to overwrite the leaflet.deflate marker icon???
var TestIcon = L.Icon.Default.extend({
options: {
iconUrl: 'assets/images/markers/business.png'
}
});
var testIcon = new TestIcon();
businessProperties.addTo(m);
// Init base maps for switch
var grayscale = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', { id: 'MapID', attribution: 'Map maintained by Demo LTD, — Map data © OpenStreetMap,' }).addTo(m);
var streets = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { id: 'MapID', attribution: 'Map maintained by Demo LTD, — Map data © OpenStreetMap,' });
var baseMaps = {
"Streets": streets,
"Grayscale": grayscale
};
// Init overlay map switch
var overlayMaps = {
"Bussines Properties": businessProperties
};
// Add switches to map control
L.control.layers(baseMaps, overlayMaps).addTo(m);
</script>
</body>
</html>
Is it possible to change the default marker icon to a custom marker icon while using the 'Leaflet.Deflate' plugin?
The answer is: No.
The current code for Leaflet.Deflate uses a default marker and a default marker only, see https://github.com/oliverroick/Leaflet.Deflate/blob/991f51ca82e7bb14a17c8d769b4c378c4ebaf700/src/L.Deflate.js#L42
You could hack your way around it, but I would rather recommend filing a feature request in the Leaflet.Deflate repo. It should be possible to modify the Leaflet.Deflate repo to allow line/polygon features to have some extra properties to be used as marker options.
I have the following plotly code:
var element = document.getElementById(scope.changeid);
function getData(division,redraw) {
var employeeData = [];
if (!division) {
$http.get(api.getUrl('competenceUserAverageByMyDivisions', null)).success(function (response) {
processData(response,redraw);
});
}
else {
$http.get(api.getUrl('competenceUserAverageByDivision', division)).success(function (response) {
processData(response,redraw);
})
}
}
function processData(data,redraw) {
var y = [],
x1 = [],
x2 = [];
data.forEach(function (item) {
y.push(item.user.profile.firstname);
x1.push(item.current_level);
x2.push(item.expected);
});
var charData = [{
x: y,
y: x1,
type: 'bar',
name: $filter('translate')('COMPETENCES.GRAPH.CURRENT'),
marker: {
color: '#23b7e5'
}
}, {
x:y,
y:x2,
type: 'bar',
marker: {
color: '#f05050'
},
name: $filter('translate')('COMPETENCES.GRAPH.EXPECTED')
}],
layout = {
title: $filter('translate')('USERMANAGEMENT.USERS'),
barmode: 'stack',
legend: {
traceorder: 'reversed'
}
};
Plotly.newPlot(element,charData,layout);
}
scope.$watch('divisionId', function (newValue, oldValue) {
if (newValue) {
getData(newValue.id,true);
}
}, true);
getData(null,false);
This generates the following chart:
<div class="panel-body">
<h4 class="text-center">{{'COMPETENCES.GRAPH_TITLES.OVERVIEW_GAP' | translate}}</h4>
<vertical-bar-chart id="chartArea" goto="competence.titleCompetenceDetails"
changeid="chartArea" xcolumn="xColumn" y-column="yColumn"
dataset="dataSet"
has-addition="true"
style="width: 80%; text-align: center"></vertical-bar-chart>
</div>
As you might be able to tell the text (x column) is being unintentionally cut off. So my question is how can i avoid this? i have attempted to increase the height of the element however without any luck :(
AS you can see here:
(oh you cant tell because of the white background but the height of panel body is 1000 px however it still cuts it off.)
Try increasing the bottom margin in layout.margin.b (more info in the plotlyjs reference page.
For reference, I had the same issue, margin bottom didn't help, but after the graph was created, I ran the following JQuery which revealed the hidden text:
var heightincrease = $('#yourID').find('.svg-container').height() + 100;
$('#yourID').find('.svg-container').height(heightincrease).find('.main-svg').height(heightincrease);
Obviously adjust as required to reveal your whole graph. Will probably break resizing so will need work if that's a concern.
I have a Google Line Chart with 2 data series - Row A and Row B:
Here is the very simple test code - just open it in the browser and it will work:
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={'modules':[{'name':'visualization','version':'1','packages':['corechart']}]}"></script>
<script type="text/javascript">
var data = {"rows":[
{"c":[{"v":"C"},{"v":-43},{"v":-42}]},
{"c":[{"v":"D"},{"v":-49},{"v":-39}]},
{"c":[{"v":"E"},{"v":-49},{"v":-48}]},
{"c":[{"v":"F"},{"v":-50},{"v":-49}]},
{"c":[{"v":"G"},{"v":-57},{"v":-56}]}],
"cols":[
{"p":{"role":"domain"},"label":"MEASUREMENT","type":"string"},
{"p":{"role":"data"},"label":"Row A","type":"number"},
{"p":{"role":"data"},"label":"Row B","type":"number"}]};
function drawCharts() {
var x = new google.visualization.DataTable(data);
var options = {
title: 'How to add tooltips?',
width: 800,
height: 600
};
var chart = new google.visualization.LineChart(document.getElementById('test'));
chart.draw(x, options);
}
$(function() {
google.setOnLoadCallback(drawCharts);
});
</script>
</head>
<body>
<div id="test"></div>
</body>
</html>
I would like to add tooltips to each data point, which would for example display:
Row A: x=D y=-49
on mouse hover. And I can not use dataTable.addColumn, because my chart is generated at once by a perl script and I just use a data Object with cols and rows as above.
Does anybody please know, how to do it here?
You can use a DataView to create the tooltip columns for you. This code snippet will dynamically create a tooltip column in the DataView for every data series:
var columns = [0];
for (var i = 1; i < x.getNumberOfColumns(); i++) {
columns.push(i);
columns.push({
type: 'string',
properties: {
role: 'tooltip'
},
calc: (function (j) {
return function (dt, row) {
return dt.getColumnLabel(j) + ': x=' + dt.getValue(row, 0) + ' y=' + dt.getValue(row, j)
}
})(i)
});
}
var view = new google.visualization.DataView(x);
view.setColumns(columns);
See the working example here: http://jsfiddle.net/asgallant/xWwxP/
I'm implementing the classic mercator example (https://github.com/mbostock/d3/blob/master/examples/mercator/mercator.html), which I've changed to zoom into Afghanistan and to use only one custom slider. I'm reading in GeoJSON data of places where explosions have happened and the graph maps them all at load. I want to use the slider to view only a month of explosion points at a time but am having trouble filtering the results. I've tried several things based on posts in the Google group but fail to understand how to filter the data read in previously from 'explosions.json'. Thanks for the help!
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>IED Attacks in Afghanistan (2004-2009)</title>
<script type="text/javascript" src="../d3.v2.js"></script>
<script type="text/javascript" src="../lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../lib/jquery-ui/jquery-ui.min.js"></script>
<style type="text/css">
#import url("../lib/jquery-ui/jquery-ui.css");
body, .ui-widget {
font: 14px Helvetica Neue;
}
svg {
width: 960px;
height: 600px;
border: solid 1px #ccc;
background: #eee;
}
line {
stroke: brown;
stroke-dasharray: 4,2;
}
path {
fill: #ccc;
stroke: #fff;
}
div {
width: 960px;
}
</style>
</head>
<body>
<h3>IED Attacks in Afghanistan (2004-2009)</h3>
<script type="text/javascript">
// Create the Mercator Projection (Map)
var xy = d3.geo.mercator(),
path = d3.geo.path().projection(xy);
// Create the states variable
var states = d3.select("body")
.append("svg")
.append("g")
.attr("id", "states");
// Create the equator variable
var equator = d3.select("svg")
.append("line")
.attr("x1", "0%")
.attr("x2", "100%");
// Create the explosions variable
var explosions = d3.select("svg")
.append("g")
.attr("id","explosions");
// Load in the states & equator data from the file 'world-countries.json'
d3.json("world-countries.json", function(collection) {
states
.selectAll("path")
.data(collection.features)
.enter().append("path")
.attr("d", path)
.append("title")
.text(function(d) { return d.properties.name; });
equator
.attr("y1", xy([0, 0])[1])
.attr("y2", xy([0, 0])[1]);
});
// the variable that holds our translate, center on Afghanistan
var translate = xy.translate(); //create translation to center gride in different area
translate[0] = -1741;
translate[1] = 1487;
xy.translate(translate); // center
xy.scale(12000); //zoom in
// Load in the explosions data from the file 'explosions.json'
d3.json("explosions.json", function(collection) {
explosions
.selectAll("path") //make a path and attach data
.data(collection.features)
.enter().append("path")
.attr("d", path)
.style("stroke","red") //color the path points
.style("stroke-width",2) //size of point stroke
.attr("class","explosionpoint")
.append("title") //title is the 'name' field in the json file
.text(function(d) { return d.properties.name; });
});
</script>
<p></p>
<!-- Slider -->
<div id="scale"></div><p></p>
<script type="text/javascript">
$("#scale").slider({
min: 20040101, //min : 1/1/04
max: 20100101, //max: 1/1/10
value: 20060601, //default slider value
step: 100, // step is the allow increments the slider can move. 100 = one month
slide: function(event, ui) {
/* REMOVE ALL EXPLOSION PATHS EXCEPT FOR A PARTICULAR MONTH OR RELOAD WITH FILTERED RESULTS */
}
});
</script>
You'll need to post part or all of your explosions.json object for a concrete answer. However, something like this will filter a JSON if it's structured like {explosion1:{data1:true, data2:true}, explosion2:{data1:true, data2:false}}:
function filterJSON(json, key, value) {
var result = {};
for (var explosionIndex in json) {
if (json[explosionIndex][key] === value) {
result[explosionIndex] = json[explosionIndex];
}
}
return result;
}
(e.g. filterJSON(myjson, "data1", true) will give all explosions with data1:true)
This is not specific to d3.
Then you could use something like this for the d3-side of things:
explosions.data(myFilteredData).exit().remove(); // remove ones you don't want
explosions.enter().append("path")... // add back ones you do want
If I understand your application, it would actually be better to just toggle the visiblity attribute of the SVG elements.
var sliderrange = [20040101, 20040201]; //replace with code based on your slider
explosions.selectAll(".explosionpoint").attr("visibility", function(d) {
//Replace with the correct date comparison logic
return d.date < sliderrange[1] && d.date > sliderrange[0] ? "visible" : "hidden";
});
D3 does have a very natural way of doing this. I'll assume your data looks something like this:
[{name: explosion1name, day: 20040110,...}, {name: explosion2name, day: 20040111,...}]
...and that you've got some variable, we'll call it explosionsData, to reference the data.
You can then draw your explosions with a function that takes the values from your slider. See the .filter I've added below.
function drawExplosions(startDay, endDay) {
explosions.selectAll("path") //make a path and attach data
.data(collection.features)
.enter().append("path")
.filter( function (d) { return ( (d.day > startDay) && (d.day < endDay) )})
.attr("d", path)
.style("stroke","red") //color the path points
.style("stroke-width",2) //size of point stroke
.attr("class","explosionpoint")
.append("title") //title is the 'name' field in the json file
.text(function(d) { return d.properties.name; });
Just call this function whenever your slider values changes.