I have a chart created with Birt and I want to add some label on it to specify the 4 regions delimited by the 2 red markers (see image below), one label in each quadrant (centered if possible).
I am looking for a solution to do that, directly using birt chart editor or by using a javascript (like I have done for the red markers).
In order to dynamically center some type of label in each quadrant of the graph, you'll have to have some way of calculating the coordinates. Of course, I'm not really familiar with Birt and I'm making the assumption that the graph's red markers will vary.
Anyway, assuming that you can get the coordinates, you could write a function that would dynamically generate the label based on a couple of parameters:
function generateLabel(sContent, iXoffset, iYoffset) {
var eLabel = document.createElement('span');
eLabel.appendChild(document.createTextNode(sContent));
var eDivision = document.createElement('div');
eDivision.appendChild(eLabel);
eDivision.style.left = iXoffset + 'px';
eDivision.style.top = iYoffset + 'px';
// include other styles here...
return eDivision;
}
and then from there, you can call this function with the set label content and offset coordinates:
var eQuadrantOneLabel = generateLabel('Quadrant One', 10, 25);
// and so on...
Then just add the element to the graph's container (assuming that is has an id of, say, graph):
var eGraphContainer = document.getElementById('graph');
eGraphContainer.appendChild(eQuadrantOneLabel);
// and so on for each label...
Related
I am using chart.js to try to create a timeline of events relative to current date.
The horizontal bar chart is close but would like to show only the tips of the bars eg as points which would pretty much be a horizontal line chart.
I have shown my horizontal bar chart along with a mock-up of what it would look like as horizontal line chart.
Is this possible with chart.js ?
You first need to know that every information that you can edit about the chart is stored in the variable containing your chart (called myChart usually, but my2Bar in your fiddle).
If you want to globally change the graph, you will need to edit attributes in myChart.config.options.
If you want to change a specific chart, you will need to edit attributes in myChart.config.data.
In this case, you need to change a specific chart (which is the horizontal bar).
If you happen to check the logs of your graph, and go very deep in the config, you will finally see that bars in your chart are drawn using attributes stored in myChart.config.data.datasets[0]._meta[0].data[n]._model (n being the nth rectangle drawn, top to bottom).
Some attributes you can find there :
base : The X position where the rectangle is starting to be drawn (0 in your xAxe for instance).
x : The rectangle is being drawn until this X position.
height : The height of the drawn rectangle.
and so on ...
To edit these values, you just need to loop in your different rectangles (the n in the above path).
But you just can't do it manually on the config of your variable. If you do this, it won't work since your chart is responsive (on resize, it will redraw the chart using the former options).
What you must use are Chart.js plugins.
Plugins let you handle all the events that are triggered while creating, updating, rendering your graph.
Then, in your beforeRender event (triggered after the initialisation, but before the drawing), you need to loop in your different rectangles to edit the values to affect how they will be drawn :
beforeRender: function(chart) {
for (var i = 0; i < chart.config.data.datasets[0]._meta[0].data.length; i++) {
// Change both `3` values to change the height & width of the point
chart.config.data.datasets[0]._meta[0].data[i]._model["base"] = chart.config.data.datasets[0]._meta[0].data[i]._model["x"] + 3;
chart.config.data.datasets[0]._meta[0].data[i]._model["height"] = 3;
}
}
Here is a jsFiddle with the final result.
Unfortunately, I wasn't able to make round dots, instead of squared ones.
Update :
I have also made another jsFiddle where all the dots are linked together which makes it look like it is a horizontal line chart (can be improved of course, but it is a good start).
This is in response to the following question, How to remove padding in c3.js?, where the answer that was provided solves this issue, but also raises another issue -- the buttons on the graph are cut off at the end --
How would I get there to be no padding and the buttons not to be cut off, for example, it should look like:
The dots are getting clipped off because of the clip-path set on the chart layer. You just have to remove it. You can use D3 for this, like so
d3.select(chart.element).select("." + c3.chart.internal.fn.CLASS.chart).attr("clip-path", null);
where chart is your C3 chart object
Fiddle - http://jsfiddle.net/zds67nh1/
However you most probably want the dots to appear above the axis layer. For that you need to detach and attach the chart layer (in SVG, the z-index is determined by the order - the last of the siblings come on top. So you have to basically move it to the end of the siblings list), like so
var chartLayer = d3.select(chart.element).select("." + c3.chart.internal.fn.CLASS.chart);
var chartLayerParentNode = chartLayer.node().parentNode;
var chartLayerNode = chartLayer.remove();
chartLayerParentNode.appendChild(chartLayerNode.node());
chartLayer.attr("clip-path", null);
Fidle - http://jsfiddle.net/7e1eL22f/
So I've been using the Google Earth API and have attempting to change the image of the place marks upon mouseover.
I would like to use flag-sprites to minimize the load but I'm having trouble working out the syntax. The usage is pretty straight forward if I were to drop it straight into the HTML file.
Example:
<img src="blank.gif" class="flag flag-cz" alt="Czech Republic" />
But when it comes to putting it into my javascript file, I'm a bit lost. Here's my code:
var placemark = ge.createPlacemark('');
placemark.setName(country.name);
ge.getFeatures().appendChild(placemark);
google.earth.addEventListener(placemark, "mouseover", function(event){
placemark.setAttribute("class", "flag flag-cz");
placemark.setAttribute("alt", "Czech Republic");
placemark.setAttribute("src", "blank.gif");
});
I also attempted to do this using the highlightIcon technique but the "setAttribute" method breaks the code (since highlightIcon is a different variable type than placemark):
//Create highlight style for style map.
var highlightStyle = ge.createStyle('');
var highlightIcon = ge.createIcon('');
highlightIcon.setHref('http://google-maps-icons.googlecode.com/files/world.png');
highlightIcon.setAttribute("class", "flag flag-cz");
highlightIcon.setAttribute("alt", "Czech Republic");
highlightIcon.setAttribute("src", "blank.gif");
highlightStyle.getIconStyle().setIcon(highlightIcon);
highlightStyle.getIconStyle().setScale(15.0);
You can achieve what you want but not with CSS, since GE Plugin lives in a different realm.
First you define the sprite as the Placemark Icon, then you must explicitly set its offset in the X and Y axis.
Something like:
var icon = window.ge.createIcon('');
icon.setHref("http://opengameart.org/sites/default/files/styles/watermarked/public/last-guardian-sprites_0.png");
// Set the size of the ICON - according to the dimension on the sprite
icon.setW(50);
icon.setH(50);
// Set its offset
icon.setX(10);
icon.setY(50);
var placemark = ge.createPlacemark('');
var style = ge.createStyle(''); //create a new style
style.getIconStyle().setIcon(icon); //apply the icon to the style
placemark.setStyleSelector(style); //apply the style to the placemark
// Set the placemark's location.
var point = ge.createPoint('');
point.setLatitude(window.lat);
point.setLongitude(window.lon);
placemark.setGeometry(point);
window.ge.getFeatures().appendChild(placemark);
Also the Y axis begins in the bottom left corner of your Image.
In order to use flag-sprites you will have to re-map all the offsets (x,y), for each country flag, in your code and use those offsets when loading the placemark.
Here is the reference for the KmlIcon: https://developers.google.com/earth/documentation/reference/interface_kml_icon
I am using the Raphael library from http://raphaeljs.com/ and work on a chart library. For this library it is useful when the Y-axis are inverted. Now 0,0 is at the top left but I want it to be at the bottom left.
There is a possibility to apply a scale matrix to an element but I want the coordinates to be inverted for whatever I draw. Any clues?
The only way I could figure out to do this was to apply a negative scaling to the svg element using CSS (see this fiddle). (I used jQuery to add the styles).
This is not without problems, though. For example, text is going to be mirrored, unless you do something to un-mirror it (like applying the invert() method I added to elements using Raphael.el):
Raphael.el.invert = function() {
this.transform('s1,-1');
};
Also, if you are going to be interacting with the elements using your mouse, you will have to tweak things. Note that the black circle uses a pretty standard mouseMove function, but it doesn't work - it moves in the wrong direction in y. So you have to do something like I did with the other circles:
function cMove(dx, dy, x,y) {
this.attr('cx', x);
this.attr('cy', paperHeight - y);
};
In short, this is not at all elegant, and no other things I tried were really any better. I know this isn't what you want to hear, but I would recommend getting used to the coordinate system as it is, unless you just plan on displaying static charts.
One small issue with Mike C's resolution is that you have to know if the text is going to be inverted in the end when you create the text. If you want to ensure right-side-up text at the end (after applying other transformations) I found it works well to alter the text element's .transform() to flip the scale of text to right side up at the end.
function InvertText(ObjSet){
// This function resets the inversion of text such that it is always right side up
// ObjSet is a raphael paper.set() object
for (var i=0; i<ObjSet.items.length; i++){
var ThisObj = ObjSet.items[i];
if (ThisObj.type == 'text'){
var tArr = ThisObj.transform();
// Find the scaling factor
for (var j=0; j<tArr.length; j++){
if (tArr[j][0] == 's'){
tArr[0][2] = 1;
break;
}
}
ThisObj.transform(tArr);
}
}
}
You can use like this:
var ObjSet = paper.set().push(
paper.text(0,10,'FirstText'),
paper.path('M,0,0,v,100,h,20,v,-100,h,-20'),
paper.circle(0,0,5)
);
//Flip everything on the y-axis
ObjSet.transform('s,1,-1, T,100,100');
// Make the text right-side-up
InvertText(ObjSet);
Here's how to do it just with RaphaelJS transforms, no CSS transforms.
var SCALE = 2;
var paper = Raphael(0, 0, 400, 700);
// box notched at bottom-center and right-center
var p = paper.path("M0,0 L100,0 L100,40 L90,50 L100,60 L100,100 L60,100 L50,90 L40,100 L0,100 Z");
var bounds = p.getBBox();
p.attr({
stroke: 'none',
fill: [90, '#578A6E', '#34573E'].join("-")
})
.transform("t"+ (-bounds.width/2) +","+ (-bounds.height/2) +
"s"+ SCALE +","+ (-SCALE) +
"t"+ (bounds.width/2) +","+ (-bounds.height/2));
Raphael applies scale transforms from the center of the element's bounding box, rather than its origin. To invert the y-axis, offset before scaling, then offset again after.
I'm building in some custom functionality where users can click on data points in a line chart to add notes to that date. This is a bit misleading as the notes aren't actually attached to the metrics themselves but rather the date it lands on. In other words, if I have 6 series on one line chart that spans the dates 01/01/12 - 01/08/12, a single note on 01/05/12 will apply to all 6 series. So, as you can imagine clicking on a data point on one of the 6 series or the date 01/05/12 would mislead the user to believe that this note would be applied to that data point, not the entire date and any series that lands on that date.
So, to remedy this usability issue I've decided that the best visual cue would be something like this:
There would be a clickable icon at the top of each xAxis gridLine that would need to scale with the xAxis gridLine (like if a user selects an area to zoom in on).
Suggestions on best way to pull this off? I only need a suggestion for how best to add the icon to every line... I have all post-click functionality already built.
Building on Mark's suggestion using redraw event to position the images and using load event to create them. Adding them on load is necessary to make them available during export and you would not want to create new images on each redraw either.
These chart events are used:
events: {
load: drawImages,
redraw: alignImages
}
In the drawImages function I'm using the inverse translation for the xAxis to position the images on the chart:
x = chart.plotLeft + chart.xAxis[0].translate(i, false) - imageWidth / 2,
y = chart.plotTop - imageWidth / 2;
and then adding them and setting a click handler, zIndex, pointer cursor:
chart.renderer.image('http://highcharts.com/demo/gfx/sun.png', x, y, imageWidth, imageWidth)
.on('click', function() {
location.href = 'http://example.com'
})
.attr({
zIndex: 100
})
.css({
cursor: 'pointer'
})
.add();
In alignImages the attr function is used to set new x and y values for the images which are calculated the in the same way as in drawImages.
Full example on jsfiddle
Screenshot:
Couple of ideas. First, I would use the chart redraw event to know when the chart is being redrawn (say on a zoom). Then second, explicitly place your images at the axis locations of interest. To get those query directly out of the DOM.
Using jQuery:
$('.highcharts-axis') //return an array of the two axis.
They will have svg "text element" children with (x, y) positions.