Creating multiple distinct path elements in D3JS - d3.js

Let us consider this simple data:
var data = [
{
"id": "A",
"geometry": [ [0, 0], [10, 10], [10, 20], [0, 20] ]
},
{
"id": "B",
"geometry": [ [10, 10], [25, 10], [25, 30], [10, 20] ]
},
];
I'd like to display "A" and "B" using a distinct area for each, as doing so will let me apply a class to them (useful because I want them to use different background colors and to react separately to clicks and mouse hovers.)
I'm able to use d3.svg.area() to draw a continuous graph however it assumes that "the input data is a two-element array of numbers" (not my case) and it does not seem to support the drawing of distinct areas.
What is the pattern for it?
UPDATE
I'm using polygons in the sample data for simplicity. Overall, the goal however is to produce a stream that be composed of several areas instead of just a single one. Best illustrated with the picture below:
I'll update the post if more details are needed.

Hard to understand what exactly you mean by wanting to draw distinct areas. Do you mean that you want to end up with 2 path elements –– one for the geometry of the object with id:"A" and the other for id: "B"? If so:
var pathGenerator = d3.svg.area()
var paths = d3.select("svg").selectAll("path").data(data);
paths.enter()
.append("path")
.attr("class", function(d) {
if(d.id == "A") { return 'class-A'; }
else if(d.id == "B") { return 'class-B'; }
})
.attr("d", function(d) {
return pathGenerator(d.geometry);
});

Related

Could someone explain vertices.flatten! in SketchUp Ruby to me?

Below is a piece of code (credit to Rafael Rivera) which plots points at the vertices of a model in SketchUp.
def pointplot
model = Sketchup.active_model
entities = model.active_entities
selection = model.selection
edges = selection.grep(Sketchup::Edge)
if edges.empty?
msg = 'Select one or more edges before using this tool.'
UI.messagebox(msg)
return
end
vertices = []
edges.each { |edge| vertices << edge.vertices }
vertices.flatten!
vertices.uniq!
vertices.each { |vertex| entities.add_cpoint vertex.position }
end
def check_line
sel = Sketchup.active_model.selection
ok = sel.find { |e| e.typename == "Edge" }
ok ? MF_ENABLED : MF_GRAYED
end
UI.add_context_menu_handler do |menu|
menu.add_separator
item = menu.add_item("Point Plot") { pointplot }
menu.set_validation_proc(item) {check_line}
end
Could someone please explain to me this line of code, what it actually does and why it's necessary for the code to work.
vertices.flatten!
I am aware what ".flatten!" does under normal circumstances. I understand this example perfectly from the rubyapi.org
a = [ 0, [ 1, [2, 3], 4 ], 5 ]
a.flatten!(1) # => [0, 1, [2, 3], 4, 5]
a = [ 0, [ 1, [2, 3], 4 ], 5 ]
a.flatten!(2) # => [0, 1, 2, 3, 4, 5]
a = [ 0, [ 1, [2, 3], 4 ], 5 ]
a.flatten!(3) # => [0, 1, 2, 3, 4, 5]
[0, 1, 2].flatten!(1) # => nil
But in the world of SketchUp, what does ".flatten!" actually do?
I 'put' the vertices array to my console and I see this as output.
#<Sketchup::Vertex:0x00000180a0788440>
#<Sketchup::Vertex:0x00000180a0788418>
#<Sketchup::Vertex:0x00000180a07883c8>
#<Sketchup::Vertex:0x00000180a07883a0>
#<Sketchup::Vertex:0x00000180a0788440>
#<Sketchup::Vertex:0x00000180a0788418>
#<Sketchup::Vertex:0x00000180a07883c8>
So what am I 'flattening' exactly?
Thanks!
It does exactly the same as the behavior you already observed with flatten with the only difference that it changes the object on which it is called instead of returning a changed object.
Let's have a look at these three lines:
vertices = []
edges.each { |edge| vertices << edge.vertices }
vertices.flatten!
First, there is an empty array created. Then by iterating over all edges the edges' vertices (which are very likely are stored in an array) are added to the array. That means after this line you have a nested array of vertices that looks like this (pseudo-code):
[[vertice_1, vertice_2], [vertice_3, vertice_4], [vertice_1, vertice_4]]
vertices.flatten! will then flatten the vertices to:
[vertice_1, vertice_2, vertice_3, vertice_4, vertice_1, vertice_4]

d3.js polygon strip on a cylinder spanning more than 360°

Following this question,
D3.geo : Spherical arcs rather than straight lines for parallels?
I was able to draw projected polygons (1) by using path:
var poligono = {
"type": "Polygon",
"coordinates": [
[[0, 0], [0, 20], [100, 20], [100, 0], [0, 0]]
]
}
// [...]
svg.append("path")
.attr("class", "arc")
.attr("d", path(poligono));
and unprojected rectangles (2) using .outline():
var arc = d3.geoGraticule()
.extentMajor([[0, 0], [-20, -20]])
// [...]
svg.append("path")
.attr("class", "arc")
.attr("d", path(arc.outline()));
What is the best way to draw a polygon such as the blue one in the figure (3), i.e. an unprojected polygon enrolling the cylinder for more than 360°?
(The general goal is to draw this kind of surfaces on a generic cylinder, and not specifically on a geographic projection)

How to match a position in an array to a scale in d3

Horrific title, I apologise.
Can someone please help me understand how to achieve the following.
I have a lovely routine that plots a scale, on a map, which is quite customisable in terms of it's orientation, number of colours, etc, the core of which is as follows:
var heatmapColour = d3.scale.linear().domain(d3.range(0, colours.length, 1.0 / (colours.length - 1))).range(colours);
var c = d3.scale.linear().domain(d3.extent(legendDomain)).range([0, 1]);
And if I have an array of colours as the following:
var colours = ["#6363FF", "#6373FF", "#63A3FF", "#63E3FF", "#63FFFB", "#63FFCB", "#63FF9B", "#63FF6B", "#7BFF63", "#BBFF63", "#DBFF63", "#FBFF63", "#FFD363", "#FFB363", "#FF8363", "#FF7363", "#FF6364"];
My routine can generate a legend of Red, Yellow, Green, Cyan and Blue if I set the legendDomain to have 5 entities, or if I set it to 3, would be simply Red, Green, and Blue, etc, etc.
It works perfectly.
However I'm now trying to match some countries to his legend which are based on their order in an array.
So for example, and for simplicity, if I have 50 countries and my legend set to draw 5 colours, I want the first 10 to be Red, the next 10 Yellow, the next 10 Green, etc, etc.
Using the same method to create the legend simply doesn't produce the same results.
Where my legend creates a range of colours within a domain, I now want to kind of go into this backwards and return a colour if 'something' (in this case it's order in the array) falls between the colour values.
Can someone please explain on how to achieve this.
I did something like this:
var colors = ['red', 'green', 'blue', 'brown'];
var countries = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M"];
var heatmapColour = d3.scale.ordinal().range(colors);//i used ordinals instead of linear
countries.forEach(function(d, i){
console.log(d,heatmapColour(Math.floor((i)/colors.length)))
});
working code here
A bit long for a comment, but is this what you are trying to achieve?
// Example arrays
var cols = ['c1', 'c2', 'c3'];
var ctry = [1, 2, 3, 4, 5, 6, 7, 8, 9];
function getCol(cols, i) {
return cols[Math.floor(i / cols.length)];
}
ctry.map(function(d,i) { console.log(getCol(cols, i)); });
(essentially divide index by length of color array and floor).
Fiddle

Ad hoc D3 function

I have n columns of data that I would like to make D3 scatter plots from. It takes 2 columns to make a plot so n columns would end up n-permutations of 2 times of the same code that I would like to make a plot function from. Problem is I don't know how to set the variable in D3 fashion. An example of data and the snippet of code are bellow. I appreciate for any hint. Thanks.
// Potentially, there are a lot more columns.
var data = [
{"col1": 34, "col2": 54, "col3": 345, "col4": 35, "col5": 52},
{"col1": 75, "col2": 98, "col3": 917, "col4": 03, "col5": 47},
{"col1": 63, "col2": 23, "col3": 236, "col4": 34, "col5": 78},
{"col1": 23, "col2": 38, "col3": 198, "col4": 12, "col5": 18},
{"col1": 57, "col2": 48, "col3": 274, "col4": 67, "col5": 39},
{"col1": 65, "col2": 12, "col3": 381, "col4": 27, "col5": 45}
];
// The code is long
// I just list here the parts that are involved
// How to replace FOO and BAR with a pair of parameters "col1", "col2", etc ...
var x = d3.scale.linear().range([0, width]),
y = d3.scale.linear().range([height, 0]);
x.domain(d3.extent(data, function(q) {return q.FOO;}));
y.domain(d3.extent(data, function(q) {return q.BAR;}));
svg.selectAll("circle")
.data(data)
.enter().append("circle")
.attr("r", 4)
.attr("cx", function(d) {return x(d.FOO);})
.attr("cy", function(d) {return y(d.BAR);})
.style('fill', 'blue');
EDIT:
I took a hint from mgold and make a function to rebuild the original dataset to a 2 columns dataset instead of a function of drawing scatter plot. It may not be elegant but just works. :) Thanks!
function get2Columns(foo, bar) {
var tempArr = [];
for (var i = 0; i < data.length; i++) {
tempArr.push({"FOO":data[i][foo],"BAR":data[i][bar]});
}
return tempArr;
}
data = get2Columns('col1', 'col2');
I'm not sure exactly what you're looking for, but I can try to talk about things that I think are relevant and hopefully give you some leads.
I would represent your data as an array of arrays, rather than an array of objects. This makes it much more amenable to array methods, from both vanilla JavaScript and D3. The transpose method may be especially useful, coupled with some mapping and reducing, for finding extents.
There are a handful of ways to choose two elements from an array or similar data structure. Zipping takes a pair of arrays and returns an array of pairs: the 0th items together, the 1st items together, and so on. Pairing takes one array and returns sequential pairs. Both of these have D3 implementations among those array methods.
Closer to what you want (maybe?) is the product of arrays, which given n arrays is every way to pick 1 item from each. This is typically represented as an array of arrays where the inner arrays have length n. There doesn't seem to be a D3 function to do this (I'd support adding one) but you can see this SO post. (No luck with Underscore either.)
But since you already have your matrix, maybe all you need to know is that functions like attr pass their second argument (when a function) not just the datum d but also the index i. You can also try using each and/or forEach to go from an array of values to individual values, keeping the indices distinct to get your 2D coordinate.
(On reread, the last paragraph is likely to be the most helpful.)

Different Data Lines on Kendo UI Graphs

I have a Kendo UI line chart.
This has x axis intervals of 28 days and data plotted against this every 28 days.
I want to know if its possible to add a second line but with data plotted daily rather than every 28 days.
Thanks
Yes, you can! This type of series are called scatterLines and basically for each series you have to provide an array of pairs with the x and y values.
If for the first series you provide values 0, 28, 56... and for the second 0, 1, 2... You get what you want.
Example:
$("#chart").kendoChart({
series: [
{ type: "scatterLine", data: [[0, 4], [28, 2], [56, 3]] },
{ type: "scatterLine", data: [[1, 2], [2, 3]] }
]
});
Check it here: http://jsfiddle.net/U7SvD/

Resources