Bubble chart in d3.js - html

http://plnkr.co/edit/7aw93EnMyCR3HjTu1uHa?p=preview
I have added the working fiddle.
I need to plot bubble chart on the basis of "profit" value in properties of "taluks.geojson" file.
This is working for flare.json but not for taluks.geojson file.
I have tried modifying the code in index.html as-
d3.json("taluks.geojson", function(error, root) { if (error) throw error;
var node = svg.selectAll(".node") .data(bubble.nodes(classes(root)) .filter(function(d) { return !d.properties; }))
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) { if (node.properties) node.properties.forEach(function(child) { recurse(node.name, child); }); else classes.push({packageName: name, className: node.NAME_2, value: node.profit}); }
recurse(null, root); return {properties: classes}; }
But the code is not working for taluks.geojson but only working for flare.json.
Please help regarding how to plot bubble chart on the basis of profit in properties of taluks.geojson file.
Please kindly suggest any further modificatons.
Thank you.

To render bubble chart or tree chart data should be in relational format parent and child, If you see flare.json data
{
"name": "flare",//root parent or level 0
"children": [
{
"name": "analytics",//child node or level 1
"children": [
{
"name": "cluster", //child node to analytics or level 2
"children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
}]
}]
}
/*
* In the above the data is hierarchical here flare will be like parent and children are in children array, name will be name of the node and at the end node or leaf node we don't have any children so here we reached to leaf
and taluks.geojson is not having the structure of desired manner. So we have to make it into our desired structure.
*
*/
var data= d3.nest().key(function(d){ return d.properties.NAME_2;}).key(function(d){ return d.properties.NAME_3;}).entries(root.features);
/*
* d3.nest function will perform the grouping operation mentioned/returned by the key function, this function is used to group elements by using key and it takes the data from entries function.
* so as per above code we are grouping by NAME_2 which is district name and again we are grouping that data based on NAME_3
* from above we'll get data in below format
* [{"key":"East Godavari","values":[
* {"key":"Kottapeta","values":[
* {"type":"Feature","properties":{"NAME_2":"East Godavari","NAME_3":"Kottapeta","profit":326,"npa":174},
* "geometry":{"type":"MultiPolygon","coordinates":[
* [[[81.75195312500006,16.772489547729492],[81.76336669921892,16.7611598968507],[81.7764129638673,16.755041122436637],[81.76336669921875,16.7611598968507],[81.75195312500006,16.772489547729492]]]
* ]}}]
* },
* {"key":"Rajahmundry","values":[{"type":"Feature","properties":{"NAME_2":"East Godavari","NAME_3":"Rajahmundry","profit":1762,"npa":1683},"geometry":{"type":"MultiPolygon","coordinates":[
* [[[81.71717071533203,17.0606307983399],[81.72284698486357,17.047969818115348],[81.72709655761736,17.035369873046875],[81.72931671142607,17.02490997314453],[81.73168945312517,17.009309768676758],[81.73249053955084,16.990680694580135],[81.731689453125,17.009309768676758],[81.7293167114259,17.02490997314453],[81.72709655761719,17.035369873046875],[81.7228469848634,17.047969818115348],[81.71717071533203,17.0606307983399]]],
* ]}}]
* },
* {"key":"Rampa Chodavaram","values":[{"type":"Feature","properties":{"NAME_2":"East Godavari","NAME_3":"Rampa Chodavaram","profit":376,"npa":362},"geometry":{"type":"MultiPolygon","coordinates":[[[[81.64000701904303,17.217769622802678],[81.63854217529308,17.24398994445812],[81.64405822753918,17.25922966003418],[81.64591217041021,17.293151855468864],[81.64405822753935,17.25922966003418],[81.63854217529325,17.24398994445812],[81.64000701904303,17.217769622802678]]],
* [[[81.51127624511724,17.463871002197266],[81.51648712158232,17.458469390869084],[81.52410888671903,17.454042434692326],[81.53122711181658,17.4517498016358],[81.55007171630854,17.447589874267692],[81.5312271118164,17.4517498016358],[81.52410888671886,17.454042434692326],[81.51648712158214,17.458469390869084],[81.51127624511724,17.463871002197266]]]]}}]
* },
* {"key":"Razole","values":[
* {"type":"Feature","properties":{"NAME_2":"East Godavari","NAME_3":"Razole","profit":1185,"npa":1141},"geometry":{"type":"MultiPolygon","coordinates":[
* [[[81.6993026733399,16.41020011901867],[81.70881652832048,16.383939743041992],[81.7134628295899,16.35638809204113],[81.70881652832031,16.383939743041992],[81.6993026733399,16.41020011901867]]],
* ]}}]
* }
* .........
* ]}]
To know more about d3 nest function go this link https://github.com/mbostock/d3/wiki/Arrays
* above structure is generated by d3.nest function, now we need to arrange that data into parent child hierarchical data, for this we are using below code
*/
var myData={name:'flare', children:[]};//this will be root
data.forEach(function(distc){
var dis={};
dis.name=distc.key;//here distc will hold one level write console.log(distc); and observe the data
dis.children = [];
myData.children.push(dis);
distc.values.forEach(function(chil){
var chis={};
chis.name=chil.key;
// chis.children=chil.values;
chis.size=chil.values[0].properties.profit;//we are using size as value in recurse function of classes function and we want to display bubble chart on profit value
dis.children.push(chis);
});
});
console.log(myData);//myData has the desired structure
root=myData;//assigning that json to root variable
I think you know what happens with the rest of the code.
Hope everything is clear, If not ask me for more.
:D
In order to create bubble chart we are using d3 pack layout, this layout we are generated at
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameter])
.padding(1.5); and this bubble calling nodes at this line
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root))//here bubble.nodes takes the json, that json should be in leaf structure, nodes function will generate relational data, write console.log(bubble.nodes(classes(root))); Here we are using classes function, because flare.json has already relational format json but bubbles.nodes need leaf structure so in classes function we are converting that flare.json relational structure into leaf
.filter(function(d) { return !d.children; }))
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

Related

D3 Line Generator Handling Multiple Arrays

I have 3 arrays within a JSON file of the following structure:
I am trying to parse a JSON file of coordinate data into something that can be read by D3 path/line generators. I first had to find a way to ensure the values were actual numbers and not strings. A full discussion can be found here:
D3/JS mapping a JSON data callback
That discussion prompted me to not only consider formatting the JSON data via .map() to numbers, but also consider a nested/zipped format that the line generator can make sense of. That is really the target I've been after all along. As depicted above, My JSON has 3 arrays, xs, ys and id. id only governs color, and takes 1 of 3 values (0,1,2). I was recommended this approach:
var obj = raw_json[0];
var data = obj.id.map((id, i) => [+id, +obj.xs[i], +obj.ys[i]]);
My line generator function is:
var valueLine = d3.svg.line()
.x(function(d) {return xScale(d.xs);})
.y(function(d) {return yScale(d.ys)});
However I am getting tens of thousands of errors, and unfortunately I do not have that much experience with parsing related issues and I am not sure how to proceed.
Full block & JSON here.
Glad to see you took my advice on restructuring your data as we are moving in the right direction. I suggested that you should convert your three separate arrays into one array of individual arrays per point to make the use of the line generator more easy and to eliminate the need for cross-array reads to collect data for the points.
This time, though, you are not accessing your values correctly. By using function(d) { return xScale(d.xs); } you are assuming that your points were represented by objects having properties xs and ys. My suggested approach however got rid of these properties by storing the information into arrays. There are basically two ways around this:
Adjust you path generator's .x() and .y() callbacks while keeping your data structure.
var obj = raw_json[0];
var data = obj.id.map((id, i) => [+id, +obj.xs[i], +obj.ys[i]]);
// Remember, that d is a point's array [id, x, y]
var valueLine = d3.svg.line()
.x(function(d) { return xScale(d[1]); })
.y(function(d) { return yScale(d[2]); });
If you prefer to store your points' data in objects instead, another solution would be to adjust how your data is built.
// Structure data into an array of objects {id, x, y}
var obj = raw_json[0];
var data = obj.id.map((id, i) => ({
id: +id,
x: +obj.xs[i],
y: +obj.ys[i]
}));
// Keep your code as d now has properties x and y.
var valueLine = d3.svg.line()
.x(function(d) { return xScale(d.x); })
.y(function(d) { return yScale(d.y); });
I think you aren't modeling correctly the data to use to use with data(data).enter() and/or d3.svg.line(). As you defined your valueLine function it expects an array of objects with xs and ys properties and you are feeding it with an array of arrays.
Try it out manually to see how it works
valueLine([{xs: 1, ys: 1}, {xs: 3, ys: 5}, {xs: 12, ys: 21}])
and see the generated svg path:
"M100,400L300,-400L1200,-3600"
So maybe you can change how you are preparing your data for something like :
var data = obj.id.map((id,i) => ({id: +id, xs:+obj.xs[i], ys: +obj.ys[i]}));

D3js json links search using 2 attributes

so I have a page showing pathways between nodes with a json file like this:
{"nodes":[
{"name":"Node 1", "Number":"01", "x":"48.23", "y":"638.54", "Status":"starting"},
{"name":"Node 2", "Number":"02", "x":"129.05", "y":"658.49", "Status":"starting"},
{"name":"Node 3", "Number":"03", "x":"174", "y":"687.79", "Status":"starting"},
{"name":"Node 4", "Number":"04", "x":"169.96", "y":"626.92", "Status":"starting"},
{"name":"Node 5", "Number":"05", "x":"263.65", "y":"631.47", "Status":"starting"}
],
"links":[
{"source": 1, "target": 2, "value": 2},
{"source": 1, "target": 3, "value": 8},
{"source": 1, "target": 4, "value": 5},
{"source": 2, "target": 3, "value": 4},
{"source": 2, "target": 4, "value": 9}
]
}
At the moment I have it set up so that when ever I click on a node it shows all pathways to other nodes. What I want is another mode, where when I click on the node it only shows the pathway with the highest "value" attribute for that corresponding source value.
I have tried looking online for some d3js examples of something like this but haven't found anything and have no idea where to start.
atm I am using a d3.selectAll function to find all pathways for source 1 when i click on node 1 like this:
d3.selectAll(".from" + d.nodes[0].Number + ":not(.pathlabel)")
.transition()
.duration(10)
.style("stroke", "blue")
.style("display", "block")
.style("stroke-opacity", blueActive[d.nodes[0].Number])
;
My code is based on this example: http://bl.ocks.org/fhernand/9a9f93f2a6b0e83a9294
My code is on jsfiddle:
jsfiddle.net/jgs6d7fv
I just want to know how to search through my JSON file using d3.js to find a specific link based on the source and the value attributes.
I'm using the same example passed by you http://bl.ocks.org/fhernand/9a9f93f2a6b0e83a9294
This will get you all the list of link data from
d3.max(d3.selectAll(".from" + d.nodes[0].Number + ":not(.pathlabel)").data()
To get the maximum pass to a player you need to find the max pass from that is done like this
var from_max = d3.max(d3.selectAll(".from" + d.nodes[0].Number + ":not(.pathlabel)").data(), function (d) {
return (d.value);
});
To get the maximum pass to a player you need to find the max pass to that is done like this
//this will calculate the max for to
var to_max = d3.max(d3.selectAll(".to" + d.nodes[0].Number + ":not(.pathlabel)").data(), function (d) {
return (d.value);
});
Now you can display or hide the path on the basis of the max path thats done like this using the display css attribute:
d3.selectAll(".to" + d.nodes[0].Number + ":not(.pathlabel)")
.transition()
.duration(10)
.style("stroke", "orange")
.style("display", function (d) {
if (d.value == to_max) {
return "block"
} else {
return "none"
}
})
Working fiddle I have added comments here.

populating existing SVG elements with JSON data using D3

What I want to achieve is an SVG that has several separate polygons that can be coloured according to a set of JSON data. I'm nearly there, but having problems with matching up the data objects with the SVG elements in the correct order.
I've created an SVG, which has several polygons such as
<polygon id="Zone2" fill="#DFECC5" stroke="#000000" stroke-miterlimit="10" points="828.815,660.425 841.261,594.05 842.82,594.422 849.014,564.644 837.769,562.306 841.108,546.883 851.945,549.118 866.75,474.55 837.799,468.253 837.254,470.748 833.572,470.097 833.982,467.844 817.591,463.942 818.746,458.473 875.479,470.067 838.604,655.601 837.24,662.395 " style="fill: rgb(244, 246, 252);"><title>Car Park South, 1230</title></polygon>
Here's the JSON file I'm parsing:
{
"zoneData":[
{"zoneID":"Zone1","name":"Car Park South", "value":1230},
{"zoneID":"Zone2","name":"Outdoor North", "value":3453},
{"zoneID":"Zone3","name":"Outdoor West", "value":2342},
{"zoneID":"Zone4","name":"Outdoor South", "value":3453},
{"zoneID":"Zone5","name":"High St. East", "value":1023},
{"zoneID":"Zone6","name":"High St. West", "value":2322},
{"zoneID":"Zone7","name":"Car Park North", "value":4562},
{"zoneID":"Zone8","name":"Indoor North", "value":5644},
{"zoneID":"Zone9","name":"Indoor South", "value":2356}
]
}
I've managed to parse everything and create a proper colour scale, and even use that scale to set colours for each polygon, but they are in the wrong order. The script is here:
d3.json("zoneAnalysisData.json", function(error, data) {
var minDataValue = d3.min(data.zoneData, function(d) { return d.value; })
var maxDataValue = d3.max(data.zoneData, function(d) { return d.value; })
var color = d3.scale.linear()
.domain([minDataValue, maxDataValue])
.range(["#ffffff", "#0038c5"]);
d3.selectAll("[id*='Zone']")
.data(data.zoneData)
.style("fill", function(d){
return color(d.value);
})
.append("title")
.text(function(d) {
return d.name + "\n" + d.value;
});
});
I've looked at the docs on selections and thinking with joins but was not able to figure out how to match the existing elements in my selection with JSON data array elements.
The problem is that you need the polygons to be selected in a specific order, which isn't guaranteed by .selectAll(). You also can't reorder them easily, as D3's .sort() method works on data, which hasn't been bound to the elements at that point.
One way of fixing this would be "attach" some data manually to the elements that allows to match up elements and data. Then all you need is a key function:
d3.selectAll("[id*='Zone']")
.each(function() { this.__data__ = { zoneID: this.id })
.data(data.zoneData, function(d) { return d.zoneID; })
...
The second line here mimics what D3 does internally and creates the attribute that D3 looks for when trying to find bound data.

Query a backbone collection (or json) from a Filter model (or json)

I'm trying to render a google map from data I have in a LocationCollection.
Users will define filterParameters in a Model, here is the JSON from a filter
var filter = {
"id_ref_type_category":[2,4],
"pricemin":5,
"pricemax":15,
"distmin":10, // meters
"distmax":150 // meters
}
I need to query my collection, here is a sample of json I have for my location
var location = [
{
"id":"1",
"name":"Sushi 1",
"price_min":"10",
"price_max":"20",
"price_avg":"15",
"id_ref_type_category":"1",
"latitude":"48.863831",
"longitude":"2.356215"
},
{
"id":"2",
"description":"Miam ",
"price_min":"15",
"price_max":"35",
"price_avg":"25",
"id_ref_type_category":"4",
"latitude":"48.864269",
"longitude":"2.355153"
},
{
"id":"3",
"name":"Restaurant 1",
"price_min":"5",
"price_max":"20",
"price_avg":"12.5",
"street_number":"60",
"id_ref_type_category":"1",
"latitude":"48.863407",
"longitude":"2.350938"
},
{
"id":"4",
"name":"Chez gigi",
"price_min":"0",
"price_max":"17",
"price_avg":"8.5",
"id_ref_type_category":"2",
"latitude":"48.861824",
"longitude":"2.350901"
}
]
Regarding to my filter parameter, i am looking for
a location with a id_ref_type_category equal 2 OR 4
a location with an average price around 5 and 15
a location within a distance around 10 and 150 (it's in meters) (if user allow geolocation)
I can calculate the distance from my position to the location with this js function from geocoding http://www.geodatasource.com/developers/javascript
I have looked for backbone collection filtering but didn't find a lot, I have looked for json query systems, but couldn't find any stuff.
I takes any suggestions.
How about applying a underscore filter for all the attributes like in here:
http://jsfiddle.net/cnDeu/1/
I have relaxed a bit your filter object so that a location makes it through.
Essentially the filter looks like this:
var filtered = loc.filter(function (el) {
var dist = distance(el.get('latitude'), el.get('longitude'),
position.latitude, position.longitude, 'K') / 1000;
return ((el.get('id') == filter.id_ref_type_category[0]) ||
(el.get('id') == filter.id_ref_type_category[1])) &&
el.get('price_avg') >= filter.pricemin &&
el.get('price_avg') <= filter.pricemax &&
dist >= filter.distmin &&
dist <= filter.distmax;
});

Building a new Dictionary out of an old one? Help with Dictionary recursion

I'm working with a large set of hierarchical taxonomic terms, where each term ("203") has a matching "term203" movie clip on the stage, and am having trouble getting a recursive function to return all of a given term's descendants.
There is a main Dictionary() object with the following nested organization for each term:
{ [object Movie Clip] : { "tid":203, "parent":99, "name":"Culture", selected:false, "otherData":"etc" } }
...where the [object Movie Clip]'s instance name would be "term203". All of these object:subObjectArray items ("terms") are stored in a master taxonomy:Dictionary() object.
I've been trying to make a recursive function (which is in itself already a little above my head) that takes the click.target of a movie clip and returns a new Dictionary() object with all of the children and grandchildren and great grandchildren (etc) of that term, in the same, nested organization described above.
The code below traces the right number of recursive loops, but the returned Dictionary() object only contains the first run's terms (only the immediate children of the requested term).
var taxonomy:Dictionary = new Dictionary();
// ...Term info is loaded into taxonomy from a JSON-style text file)
// ...MOUSE_OVER event listeners are added to each
function revealChildren(hvr:MouseEvent):void {
trace("Spotlighting " + taxonomy[hvr.target].name + "'s children...");
for(var key:Object in getAllChildren(taxonomy[hvr.target].tid)) {
trace("Animating " + taxonomy[key].tid); // Traces only immediate children
var revealTween = new Tween(key, "alpha", Regular.easeInOut, key.alpha, 1, 1, true);
}
}
function getAllChildren(origin):Dictionary {
var children:Dictionary = new Dictionary();
for(var element:Object in taxonomy) {
if(taxonomy[element].parent == origin) {
var subSet = getAllChildren(taxonomy[element].tid);
children[element] = subSet; // *CAN'T ACCESS 'subSet' PROPERLY*
trace("Parent = " + origin);
trace("Matched! Adding " + taxonomy[element].tid + " as key and setting its value to " + subSet); // Traces correct amount of times, one for each descendent
}
else {
}
}
return children;
}
I certainly do not claim to be the most efficient AS3 programmer, so I am open to alternative configurations. However, after trying static and nested Arrays, I would prefer to continue using the Dictionary() object as my main pool.
As noted, only the immediate children end up animating in the revealChildren() function. It's mystifying to me then, that in the getAllChildren() function, all of the descendants trace sequentially (well in no particular order) in the output window.
Also I can't get any sort of name or property out of the subSet Object. That could be the problem.
I've only tested it as far as 'two generations,' but it seems that only the first round of calling the function successfully adds those terms to the new Dictionary() object and returns it intact to the animating function.
Too bad dict.filter(getDescendants) won't work. Please help!
To simplify things, I've added an output parameter called children. This is the Dictionary into which our function will store its results. It has a default value, so you don't need to specify one. In that case, it will create a new instance for itself.
function getAllChildren(origin:*, children:Dictionary = null):Dictionary {
if (children = null) children = new Dictionary();
for(var element:* in taxonomy) {
if(taxonomy[element].parent == origin) {
children[element] = taxonomy[element];
getAllChildren(taxonomy[element].tid, children);
}
}
return children;
}
When a child is discovered, it is copied over exactly: children[element] = taxonomy[element];
Next, the function calls itself recursively, supplying it the same output dictionary as it has been using.
Edit:
In response to your comment... Your code originally said this after finding a child named element:
children[element] = getAllChildren(taxonomy[element].tid);
You're making children[element] equal to a Dictionary object here. What you create is a tree structure, mapping MovieClip objects to Dictionary objects containing a similar mapping of its children. Using a for in loop on this structure will only give you the top-level children. It will not recursively traverse the entire tree.