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

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)

Related

Circle appears after pushpull on a trapezoid face

I'm new to Sketchup scripting. When I extrude face2 below, a circle appears at the base of the face. It only appears after the pushpull and it doesn't appear if I extrude face1. Face1 and face2 are supposed to be the first two walls of a box. Could someone explain this?
ents = Sketchup.active_model.entities
face1 = ents.add_face [1.m, 0, 0], [1.1.m, -0.1.m, 0], [-1.1.m, -0.1.m, 0], [-1.m, 0, 0]
face2 = ents.add_face [-1.m, 0, 0], [-1.1.m, -0.1.m, 0], [-1.1.m, 1.1.m, 0], [-1.m, 1.m, 0]
face1.pushpull(1.m, true)
Your code works for me (I also extruded face2). Could you edit your question and upload a picture of the problem you are describing?
This was a bug in Sketchup, as indicated in comments above.

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.)

Creating multiple distinct path elements in D3JS

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);
});

D3.js log scale stacked bar

I am trying to construct a single column stacked bar chart with d3.js, my data array might look like this in some instances:
[{x: 0, y:1, y0: 0}, {x: 1, y: 10, y0: 1}, {x: 2, y:2, y0: 11}]
but in another instance it might look like this:
[{x: 0, y:20, y0: 0}, {x: 1, y: 300, y0: 20}, {x: 2, y:2189, y0: 320}]
I am having trouble with the log scale. The chart's height is always 150px and the rectangles for the stacked chart should not be smaller than 10px ever.

Resources