add legend to dc.js scatterplot - d3.js

Somewhat related to this question.
I would like to add a legend to a scatterplot using dc.js, ideally with the possibility of highlighting points based on hover/click of the corresponding legend. The source code seems to indicate that color is "legendable" for scatterplots, but I cannot find any examples where this is done.
Running the code below in the browser triggers an error:
tmp.html:26 Uncaught TypeError: Cannot read property 'key' of undefined
at Object.myChart.dimension.group.colorAccessor.d (tmp.html:26)
at Object._chart.getColor (color-mixin.js:149)
at Object._chart.legendables (scatter-plot.js:360)
at Object._legend.render (legend.js:45)
at Object._chart.render (base-mixin.js:703)
at Object.dc.renderAll (core.js:230)
at tmp.html:33
<!DOCTYPE html><html><head><title></title>
<link rel="stylesheet" type="text/css" href="dc.css"/>
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.6/crossfilter.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.9/dc.js"></script>
</head><body>
<div id="chart"></div>
<script type="text/javascript">
dc.config.defaultColors(d3.schemeCategory10);
var myChart = dc.scatterPlot("#chart");
let input = [{x:0,y:0,c:1}, {x:1,y:1,c:0},{x:0.5,y:0.5,c:2}];
(function(data) {
var ndx = crossfilter(input),
dims = ndx.dimension( d => [+d.x,+d.y,+d.c]),
grp = dims.group();
myChart
.dimension(dims)
.group(grp)
.colorAccessor(d=> +d.key[2])
.x(d3.scaleLinear().domain(d3.extent(d3.extent(input, d => +d.x))))
.valueAccessor(d => +d.key[1])
.brushOn(false)
.legend(dc.legend().x(750).y(10).itemHeight(13).gap(5));
})(input);
dc.renderAll();
</script></div></body></html>

Yes, unfortunately it is a bug that was reported a long time ago, but has never been fixed. The scatter plot is calling _chart.getColor() with no arguments.
https://github.com/dc-js/dc.js/issues/1138#issuecomment-217861014
Even if it's fixed, it's only returning one legendable, so I think it will only work for the scatter-series case. If you want to display legend items for each color in your chart, you'll have to generate that data yourself:
myChart.legendables = function () {
var byColor = {};
myChart.group().all().forEach(function(d) {
var color = myChart.colors()(myChart.colorAccessor()(d));
byColor[color] = {
chart: myChart,
name: 'color ' + myChart.colorAccessor()(d),
color: color
};
})
return Object.values(byColor);
};
Maybe the chart should do something this automatically. One complication is that there currently aren't any names defined for colors - here I just pasted 'color ' with the value returned by the color accessor, but you presumably have better names in your actual data set.
Fork of your fiddle.

Related

Unable to preventDefault inside passive event listener due to target being treated as passive with D3 js

I'm working with D3.js library. I wan't to make zoom in SVG file.
When I'm zooming in or out. Has a lot of errors emit into console like this:
[Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive. See https://www.chromestatus.com/features/6662647093133312
This is my code.
<object id="map" type="image/svg+xml" data="seatMap.svg"></object>
<script type="text/javascript">
'use strict';
var obj = document.getElementById('map');
var svgDoc = obj.getSVGDocument();
var svg = d3.select(svgDoc).select('svg');
var g = d3.select(svgDoc).select('#seat-map');
svg.call(d3.zoom()
.scaleExtent([1, 8])
.on('zoom', zoomed));
function zoomed() {
g.attr('transform', d3.event.transform);
}
</script>
I already research and try many times but still get are errors.
Anyone know how to resolve? Thank you.

compare class of path elements to a dataset

I am really new to d3 and I want to make a choropleth map with the d3 datamaps so if have to compare the classes of the pathways to my dataset. So far I did manage to get the path's
html
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="/d3map/datamaps.world.min.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>
<script src="d3map.js"></script>
js code
var map = new Datamap({element: document.getElementById('container'),
})
var country = d3.selectAll("path")
Country is an array containing 177 elements
one element in country has this form:
<path d="bunch of numbers" class="datamaps-subunit AFG" style="etc"></path>
i want to see if the country code in the class (e.g. AFG) matches one in my dataset.
Country.class gives undefined
I am probably missing something really obvious
Here's an example fiddle: https://jsfiddle.net/g2v5sgmz/
You can filter specific classed elements in an array with the filter method like this:
var country = d3.selectAll("path");
var afg = country.filter(function(d,i){
if( d3.select(this).classed("AFG") ){
return d3.select(this);
}
})

How to communicate 'externally' between Adobe Animate CC animations?

From the script in the html page, I'm trying to control what happens in the Adobe Animate CC animations I've created. For example, here you'll see a script that doesn't work that's trying to tell the ship animation to gotoAndPlay(5). Anyhow, the ship animation is not responding to that. I'm guessing it's because I'm not addressing/naming it correctly. Help me talk to my animations. See the code below.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Lets talk to each other</title>
<script src="http://code.createjs.com/easeljs-0.8.1.min.js"></script>
<script src="http://code.createjs.com/tweenjs-0.6.1.min.js"></script>
<script src="http://code.createjs.com/movieclip-0.8.1.min.js"></script>
<script src="ship.js"></script>
<script src="car.js"></script>
<script>
function init () {
var canvas, stage, exportRoot;
canvas = document.getElementById("canvas_ship");
exportRoot = new libs_ship.ship();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
createjs.Ticker.setFPS(libs_ship.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
canvas = document.getElementById("canvas_car");
exportRoot = new libs_car.car();
stage = new createjs.Stage(canvas);
stage.addChild(exportRoot);
stage.update();
createjs.Ticker.setFPS(libs_car.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
}
function Tell_Canvas_Ship_to_gotoAndPlay5(){
canvas_ship.gotoAndPlay(5);
}
</script>
</head>
<body onload="init();" style="background-color:#D4D4D4">
<canvas id="canvas_ship" width="300" height="250" style="background-color:#FFFFFF"></canvas>
<canvas id="canvas_car" width="300" height="250" style="background-color:#FFFFFF"></canvas>
</body>
</html>
It looks like "canvas_ship" is the ID of your actual canvas element. I think what you are trying to do is control the content you are adding to it.
If so, you can call gotoAndPlay on the exportRoot, which is a MovieClip instance with that API.
exportRoot.gotoAndPlay(5);
The issue you will have though, is that once you create your canvas_ship stage and content, you are overwriting the variables. I recommend changing the name of the second canvas, exportRoot, and stage.
You could also add both elements to one canvas. Is there any reason you are using two canvases, other than that you used the exported content from 2 FLAs?
var stage = new createjs.Stage("canvas_ship");
var ship = new libs_ship.ship();
var car = new libs_car.car();
stage.addChild(ship, car);
createjs.Ticker.addEventListener("tick", stage);
Where is your Tell_Canvas_Ship_to_gotoAndPlay5 method getting called from? Is it a frame script in Animate/Flash?
I've received help and will now share the answer. You are welcome. Just invite me over for breakfast sometime.
In Adobe Animate, you'll need to change the library namespace (in the Publish settings in the Advanced tab I think) to lib_jerry or whatever custom name you come up with... so long as it's different than the other animation. Then just follow the setup in this code. You can call the functions from within the Animate animations.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Container</title>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<script src="tommy.js"></script>
<script src="jerry.js"></script>
<script>
var canvas, stage, tomtom, jerjer;
function init() {
var exportRoot;
//Tommy
canvas = document.getElementById("canvas_tommy");
tomtom = new lib_tommy.tommy();
stage = new createjs.Stage(canvas);
stage.addChild(tomtom);
stage.update();
createjs.Ticker.setFPS(lib_tommy.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
//Jerry
canvas = document.getElementById("canvas_jerry");
jerjer = new lib_jerry.jerry();
stage = new createjs.Stage(canvas);
stage.addChild(jerjer);
stage.update();
createjs.Ticker.setFPS(lib_jerry.properties.fps);
createjs.Ticker.addEventListener("tick", stage);
}
function button_from_tommy_was_clicked(){
tomtom.gotoAndPlay(5);
}
function button_from_jerry_was_clicked(){
jerjer.gotoAndPlay(5);
}
</script>
</head>
<body onload="init();" style="background-color:#D4D4D4;margin:0px;">
<canvas id="canvas_tommy" width="970" height="90" style="background-color:#727272"></canvas>
<canvas id="canvas_jerry" width="970" height="90" style="background-color:#727272"></canvas>
</body>
</html>

Virtual area around vml element

this is my first post, so my deepest excuses if something went wrong :)
I have a little html-control to write and biggest problem is ie6-8 support. There are no alternatives to skip ie6-8 support at all :( So after searching a while, I did found Raphael and it allows me to create custom shapes defined in SVG file. I need to attach 'mouseover' event and select element on hover. Event working great but I did find BIG problems in VML hover behavior.
Code was simplified to RAW html with VML shape.
<html xmlns:v="urn:schemas-microsoft-com:vml">
<head>
<style>v\: * { behavior:url(#default#VML); antialias: false; }</style>
</head>
<body>
<div id="message">hovered: nope</div>
<v:oval id="oval" style="width:100px; height:75px" fillcolor="#bbb"></v:oval>
<script>
var messageElm = document.getElementById('message');
var ovalElm = document.getElementById('oval');
ovalElm.attachEvent('onmouseover', function () { messageElm.innerText = 'hovered: yep'; });
ovalElm.attachEvent('onmouseout', function () { messageElm.innerText = 'hovered: nope'; });
</script>
</body>
</html>
If you try to move mouse over oval element you can noticed that rendered shape is not same as hover shape. I mean, hover triggers 2-3px from rendered shape (not from each side).
So question is: how to disable that virtual area (if it is possible at all)?
i had the same issue and i tried usemap;
first i create a map on a transparent png8 which covered the vml
this.dom.insertAdjacentHTML("AfterBegin",'<map name="'+_id+'"></map><img id="'+_id+'" src="'+transparent.png+
'" style="position:absolute;width:'+dom.clientWidth+';height:'+dom.clientHeight+'" />');
var map = this.dom.getElementsByTagName('map')[0];
this.dom.appendChild(map);
this.map = map;
then get the shape attach to an area; map it;
i made poly demo only;
function _getMap(shape){
this._map = this._map || {};
if(this._map[shape.id]){
}else if(shape.nodeName == 'shape'){
var arrDots = shape.childNodes[0].v.match(/(\d+),(\d+)/g)
this._map[shape.id] = _polyMap(arrDots);
}
return this.map[shape.id]
}
function _polyMap(arrDots){
var map = this.map;
var area = document.createElement('area');
area.setAttribute('shape',"poly");
area.setAttribute('coords',arrDots.join(','));
area.setAttribute('href','##');
area.setAttribute('alt','##');
map.appendChild(area);
}
then you can bind event on it;
function _onIE(shape, evtname, fn){
this._getMap(shape).attachEvent('on'+evtname, fn);
}

MouseOver or hover() or css() for changing positions

I have an image on my page and want that it change the position on mouseover or on click, so I tried several things but I cant find the right start.
So I started to change the top-position but that's not working, I would like that on mouseover the image it jumps to position x/y (maybe random x/y) if I mouseover it again it will jump to another position.
$('#image').one('click', function () {
$(this).css( 'top' : '+=200' );
});
But that's not working so please someone could give me some input that I can figure out what to do?
Note that while setting values for top, jQuery will not automatically get the value of top set previously and add 200 to it(like a normal programming language). You could have seen this as an error in executing if you try to see in a console. You can get the value of top first and then add 200px to it and then set it again.
This should work:
$('#image').one('click', function () {
var top = parseInt($(this).css("top"));
$(this).css( 'top' , top+ 200);
});
I had to parse the string to an integer returned by css, you could also substitute that extra operation by some method that returns only the value and not the value with "px" added to it, dunno if there is one already.
Try something like this:
<html>
<head>
<script type="text/javascript">
function switchPos(obj)
{
var divobj = document.getElementById('div1')
var x = divobj.offsetHeight - obj.height - 5;
var y = divobj.offsetWidth - obj.width - 5;
var randomx = Math.floor(Math.random()*x+1);
var randomy = Math.floor(Math.random()*y+1);
obj.style.top = randomx + 'px';
obj.style.left = randomy + 'px';
}
</script>
</head>
<body>
<div id="div1" style="width: 800px; background: red;">
<img src="image.jpg" style="position:relative;" onmouseover="switchPos(this);" onmouseout="switchPos(this);"/>
</div>
</body>
</html>

Resources