d3 - get data bound with element - javascript
The following snippet add d3.geo.circle() object as path element in an svg element. The code is:
var g = svg.append("g");
var circles = g.selectAll("path.circle");
var circle = d3.geo.circle();
circles = circles.data(d3.values(data));
circles
.enter()
.append("path");
.datum(function(d) {
return circle
.origin([d.lon, d.lat])
.angle(0.2)();
})
.attr("class", "circle")
.attr("d", path);
Now, I want to add say, a mouseOver event on these circles, which will log(prints) latitude-longitude information, I cannot access like:
g.selectAll('path.circle')
.on("mouseover", function(d,i) {
console.log(d.lat + "-" + d.lon);
});
I understand that the 'd' represents the d3.geo.circle object, but I could not figure out how to reach the data[i]?
Related
Append two elements in svg at the same level
I'm using d3.js Hi, I'm having an issue finding how to append two elements (path and image) to the same g (inside my svg) from the same data. I know how to do this, but the tricky thing is I need to get the BBox values of the "path" elements in order to place the "image" elements in the middle... My goal is actually to place little clouds in the center of cities on a map like this : this is the map I am trying to reproduce On the map it's not centered but I have to do so. So this is my current code: // Draw the map svg.append("g") .selectAll("path") .data(mapEPCI.features) .enter() .append("path") .attr("fill", d => d.properties.color) .attr("d", d3.geoPath().projection(projection)) .style("stroke", "white") .append("image") .attr("xlink:href", function(d) { if (d.properties.plan_air == 1) return ("data/page8_territoires/cloud.png") else if (d.properties.plan_air == 2) return ("data/page8_territoires/cloudgray.png") }) .attr("width", "20") .attr("height", "15") .attr("x", function (d) { let bbox = d3.select(this.parentNode).node().getBBox(); return bbox.x + 30}) .attr("y", function (d) { return d3.select(this.parentNode).node().getBBox().y + 30}) This gets the right coordinates for my images but it's because the parent node is actually the path... If I append the image to the g element, is there a way to get the "BrotherNode", or maybe the last child of the "g" element ? I don't know if I'm clear enough but I hope you get my point. I'm kinda new to js so maybe I'm missing something simple I just don't know yet Thanks for your help
I would handle your data at the g level and create a group for every map feature (country) which contains the path and a sibling image: <!doctype html> <html> <head> <script src="https://d3js.org/d3.v6.min.js"></script> </head> <body> <svg width="600" height="600"></svg> <script> let svg = d3.select('svg'), mapEPCI = { features: [100, 200, 300, 400] }; let g = svg.selectAll('g') .data(mapEPCI.features) // enter selection is collection of g let ge = g.enter().append("g"); // append a path to each g according to data ge.append('path') .attr("d", (d) => "M" + d + ",10L" + d + ",100") .style("stroke", "black"); // append a sibling image ge.append("image") .attr("xlink:href", "https://placeimg.com/20/15/animals") .attr("width", "20") .attr("height", "15") .attr("transform", function(d) { // find my sibling path to get bbox let sibling = this.parentNode.firstChild; let bbox = sibling.getBBox(); return "translate(" + (bbox.x - 20 / 2) + "," + (bbox.y + bbox.height / 2 - 15 / 2) + ")" }); </script> </body> </html>
D3 skipping first two features in a TopoJson
I am creating an interactive visualization tool with D3. I just got my map working today and I noticed that the first two features from the topojson are not loading in. The first two entries of the topojson look like this and geojson.io reads these countries in fine. (They appear on the map) "objects":{"ne_10m_admin_0_countries":{"type":"GeometryCollection","geometries":[{"arcs":[[[0,1]],[[2,3,4,5]],[[6,7]],[[8,9]],[[10]],[[11]],[[12]],[[13]],[[14]],[[15]],[[16]],[[17]],[[18]],[[19]],[[20]],[[21]],[[22]],[[23]],[[24]],[[25]],[[26]],[[27]],[[28]],[[29]],[[30]],[[31]],[[32]],[[33]],[[34]],[[35]],[[36]],[[37]],[[38]],[[39]],[[40]],[[41]],[[42]],[[43]],[[44]],[[45]],[[46]],[[47]],[[48]],[[49]],[[50]],[[51]],[[52]],[[53]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]],[[62]],[[63]],[[64]],[[65]],[[66]],[[67]],[[68]],[[69]],[[70]],[[71]],[[72]],[[73]],[[74]],[[75]],[[76]],[[77]],[[78]],[[79]],[[80]],[[81]],[[82]],[[83]],[[84]],[[85]],[[86]],[[87]],[[88]],[[89]],[[90]],[[91]],[[92]],[[93]],[[94]],[[95]],[[96]],[[97]],[[98]],[[99]],[[100]],[[101]],[[102]],[[103]],[[104]],[[105]],[[106]],[[107]],[[108]],[[109]],[[110]],[[111]],[[112]],[[113]],[[114]],[[115]],[[116]],[[117]],[[118]],[[119]],[[120]],[[121]],[[122]],[[123]],[[124]],[[125]],[[126]],[[127]],[[128]],[[129]],[[130]],[[131]]],"type":"MultiPolygon","properties":{"featurecla":"Admin-0 country","scalerank":5,"LABELRANK":2,"SOVEREIGNT":"Indonesia","SOV_A3":"IDN","ADM0_DIF":0,"LEVEL":2,"TYPE":"Sovereign country","ADMIN":"Indonesia","ADM0_A3":"IDN","GEOU_DIF":0,"GEOUNIT":"Indonesia","GU_A3":"IDN","SU_DIF":0,"SUBUNIT":"Indonesia","SU_A3":"IDN","BRK_DIFF":0,"NAME":"Indonesia","NAME_LONG":"Indonesia","BRK_A3":"IDN","BRK_NAME":"Indonesia","BRK_GROUP":"","ABBREV":"Indo.","POSTAL":"INDO","FORMAL_EN":"Republic of Indonesia","FORMAL_FR":"","NAME_CIAWF":"Indonesia","NOTE_ADM0":"","NOTE_BRK":"","NAME_SORT":"Indonesia","NAME_ALT":"","MAPCOLOR7":6,"MAPCOLOR8":6,"MAPCOLOR9":6,"MAPCOLOR13":11,"POP_EST":260580739,"POP_RANK":17,"GDP_MD_EST":3028000,"POP_YEAR":2017,"LASTCENSUS":2010,"GDP_YEAR":2016,"ECONOMY":"4. Emerging region: MIKT","INCOME_GRP":"4. Lower middle income","WIKIPEDIA":-99,"FIPS_10_":"ID","ISO_A2":"ID","ISO_A3":"IDN","ISO_A3_EH":"IDN","ISO_N3":"360","UN_A3":"360","WB_A2":"ID","WB_A3":"IDN","WOE_ID":23424846,"WOE_ID_EH":23424846,"WOE_NOTE":"Exact WOE match as country","ADM0_A3_IS":"IDN","ADM0_A3_US":"IDN","ADM0_A3_UN":-99,"ADM0_A3_WB":-99,"CONTINENT":"Asia","REGION_UN":"Asia","SUBREGION":"South-Eastern Asia","REGION_WB":"East Asia & Pacific","NAME_LEN":9,"LONG_LEN":9,"ABBREV_LEN":5,"TINY":-99,"HOMEPART":1,"MIN_ZOOM":0,"MIN_LABEL":1.7,"MAX_LABEL":6.7,"NE_ID":1159320845,"WIKIDATAID":"Q252","NAME_AR":"إندونيسيا","NAME_BN":"ইন্দোনেশিয়া","NAME_DE":"Indonesien","NAME_EN":"Indonesia","NAME_ES":"Indonesia","NAME_FR":"Indonésie","NAME_EL":"Ινδονησία","NAME_HI":"इंडोनेशिया","NAME_HU":"Indonézia","NAME_ID":"Indonesia","NAME_IT":"Indonesia","NAME_JA":"インドネシア","NAME_KO":"인도네시아","NAME_NL":"Indonesië","NAME_PL":"Indonezja","NAME_PT":"Indonésia","NAME_RU":"Индонезия","NAME_SV":"Indonesien","NAME_TR":"Endonezya","NAME_VI":"Indonesia","NAME_ZH":"印度尼西亚"}},{"arcs":[[[132,-1]],[[133,134]],[[135,-8,136,137,138,139]],[[140]],[[141]],[[142]],[[143]],[[144]],[[145]]],"type":"MultiPolygon","properties":{"featurecla":"Admin-0 country","scalerank":5,"LABELRANK":3,"SOVEREIGNT":"Malaysia","SOV_A3":"MYS","ADM0_DIF":0,"LEVEL":2,"TYPE":"Sovereign country","ADMIN":"Malaysia","ADM0_A3":"MYS","GEOU_DIF":0,"GEOUNIT":"Malaysia","GU_A3":"MYS","SU_DIF":0,"SUBUNIT":"Malaysia","SU_A3":"MYS","BRK_DIFF":0,"NAME":"Malaysia","NAME_LONG":"Malaysia","BRK_A3":"MYS","BRK_NAME":"Malaysia","BRK_GROUP":"","ABBREV":"Malay.","POSTAL":"MY","FORMAL_EN":"Malaysia","FORMAL_FR":"","NAME_CIAWF":"Malaysia","NOTE_ADM0":"","NOTE_BRK":"","NAME_SORT":"Malaysia","NAME_ALT":"","MAPCOLOR7":2,"MAPCOLOR8":4,"MAPCOLOR9":3,"MAPCOLOR13":6,"POP_EST":31381992,"POP_RANK":15,"GDP_MD_EST":863000,"POP_YEAR":2017,"LASTCENSUS":2010,"GDP_YEAR":2016,"ECONOMY":"6. Developing region","INCOME_GRP":"3. Upper middle income","WIKIPEDIA":-99,"FIPS_10_":"MY","ISO_A2":"MY","ISO_A3":"MYS","ISO_A3_EH":"MYS","ISO_N3":"458","UN_A3":"458","WB_A2":"MY","WB_A3":"MYS","WOE_ID":23424901,"WOE_ID_EH":23424901,"WOE_NOTE":"Exact WOE match as country","ADM0_A3_IS":"MYS","ADM0_A3_US":"MYS","ADM0_A3_UN":-99,"ADM0_A3_WB":-99,"CONTINENT":"Asia","REGION_UN":"Asia","SUBREGION":"South-Eastern Asia","REGION_WB":"East Asia & Pacific","NAME_LEN":8,"LONG_LEN":8,"ABBREV_LEN":6,"TINY":-99,"HOMEPART":1,"MIN_ZOOM":0,"MIN_LABEL":3,"MAX_LABEL":8,"NE_ID":1159321083,"WIKIDATAID":"Q833","NAME_AR":"ماليزيا","NAME_BN":"মালয়েশিয়া","NAME_DE":"Malaysia","NAME_EN":"Malaysia","NAME_ES":"Malasia","NAME_FR":"Malaisie","NAME_EL":"Μαλαισία","NAME_HI":"मलेशिया","NAME_HU":"Malajzia","NAME_ID":"Malaysia","NAME_IT":"Malesia","NAME_JA":"マレーシア","NAME_KO":"말레이시아","NAME_NL":"Maleisië","NAME_PL":"Malezja","NAME_PT":"Malásia","NAME_RU":"Малайзия","NAME_SV":"Malaysia","NAME_TR":"Malezya","NAME_VI":"Malaysia","NAME_ZH":"马来西亚"}},{"arcs":[[[146,147,148,149]],[[150,151,152,153]],[[154]],[[155]],[[156]],[[157]],[[158]],[[159]],[[160]],[[161]],[[162]],[[163]],[[164]],[[165]],[[166]],[[167]],[[168]],[[169]],[[170]],[[171]],[[172]],[[173]],[[174]],[[175]],[[176]],[[177]],[[178]],[[179]],[[180]],[[181]],[[182]],[[183]],[[184]],[[185]],[[186]],[[187]],[[188]],[[189]],[[190]],[[191]],[[192]],[[193]],[[194]],[[195]],[[196]],[[197]],[[198]],[[199]],[[200]],[[201]],[[202]],[[203]],[[204]],[[205]],[[206]],[[207]],[[208]],[[209]],[[210]],[[211]],[[212]],[[213]],[[214]],[[215]],[[216]],[[217]],[[218]],[[219]],[[220]],[[221]],[[222]],[[223]],[[224]],[[225]],[[226]],[[227]],[[228]],[[229]],[[230]],[[231]],[[232]],[[233]],[[234]],[[235]],[[236]],[[237]]], Here is my JS: <script>window.onload = setMap(); function setMap(){ d3.csv("/data/fdata.csv").then(function(data) { //console.log(data); d3.json("/data/a.topojson").then(function(data2) { //console.log(data2); var width = 960, height = 460; //create new svg container for the map var map = d3.select("body") .append("svg") .attr("class", "map") .attr("width", width) .attr("height", height); var projection = d3.geoNaturalEarth1() .center([0, 0]) .rotate([-2, 0, 0]) //.parallels([43, 62]) .scale(175) .translate([width / 2, height / 2]); var path = d3.geoPath() .projection(projection); d3.selectAll(".boundary") .style("stroke-width", 1 / 1); var b = topojson.feature(data2, data2.objects.ne_10m_admin_0_countries); var graticule = d3.geoGraticule(); var attrArray = ["x1","x2","x3" ]; function joinData(b, data){ for (var i=0; i<data.length; i++){ var csvRegion = data[i]; //the current region var csvKey = data[i].Country; //the CSV primary key for (var a=0; a<b.features.length; a++){ var geojsonProps = b.features[a].properties; //gj props var geojsonKey = geojsonProps.ADMIN; //the geojson primary key if (geojsonKey == csvKey){ attrArray.forEach(function(attr){ var val = parseFloat(csvRegion[attr]); geojsonProps[attr] = val; }); }; }; }; return b; }; joinData(b,data); var tooltip = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0); var currentF = "B2"; var color = d3.scaleQuantile() .domain(d3.range(0, 1000)) .range(d3.schemeReds[7]); map.append("path") .datum(graticule) .attr("class", "graticule") .attr("d", path); map.append("path") .datum(graticule.outline) .attr("class", "graticule outline") .attr("d", path); map.selectAll("path") .data(b.features) .enter() .append("path") .attr("d", path) //.style("stroke", "black") .on("mouseover", function(d) { tooltip.transition() .duration(200) .style("opacity", .9) .style("stroke-opacity", 1.0); tooltip.html(d.properties.ADMIN) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY - 28) + "px"); }) .on("mouseout", function(d) { tooltip.transition() .duration(500) .style("opacity", 0) .style("stroke-opacity", 0); }) .style("fill", function(d) { return color(d.properties[currentF]); }); }); //csv }); //json }; I believe the issue is with the map.selectAll("path") chunk. When I run it without D3 iterating through features it seems to come out fine. (With Indonesia and Malaysia still there. )
The issue is here: map.append("path") .datum(graticule) .attr("class", "graticule") .attr("d", path); map.append("path") .datum(graticule.outline) .attr("class", "graticule outline") .attr("d", path); map.selectAll("path") .data(b.features) .enter() .append("path") .attr("d", path) The two append statements create paths, you then select these existing paths when you do: map.selectAll("path"). To confirm, before you append the features, you can do console.log(map.selectAll("path").size()) to see how many elements are selected, it should be two. An enter selection can be used to create elements in the DOM for each item in the data array that does not have a corresponding element in the DOM. As there are two paths in your selection, the first two items in the data array correspond to these two elements, and thus do not need to be entered: they already exist (By binding the data with .data() you are binding the first two datums in the data array to the already existing paths). D3 doesn't "know" that these paths aren't supposed to be affiliated with those data array items. In order to ensure all items are entered you can make sure that you have an empty selection before you bind data: map.selectAll(null).data()... map.selectAll().data()... map.selectAll(".className").data()... // where no element has that class yet
d3.js packing bubble chart elements
I am trying to use the bubble chart example as a template to build a visualisation. I have my JSON as a flat-hierarchy, such that there is one element called children and that holds an array of objects that I want to visualise. The JSON looks like this: { "children":[ { "acc":"Q15019", "uid":"SEPT2_HUMAN", "sym":"SEPT2", "name":"Septin-2", "alt_ids":"", "ratio":0.5494271087884398, "pval":0.990804718 }, ..., { "acc":"Q16181", "uid":"SEPT7_HUMAN", "sym":"SEPT7", "name":"Septin-7", "alt_ids":"", "ratio":1.1949912048567823, "pval":0.511011887 } ] } I have modified the example code as follows: var diameter = 960, format = d3.format(",d"), color = d3.scale.quantile().range(colorbrewer.RdBu[9]); var bubble = d3.layout.pack() .sort(null) .size([diameter, diameter]) .padding(1.5); var svg = d3.select("body").append("svg") .attr("width", diameter) .attr("height", diameter) .attr("class", "bubble"); d3.json("datagraph.json", function(datagraph) { var node = svg.selectAll(".node") .data(bubble.nodes(datagraph)) .enter().append("g") .attr("class", "node") .attr("id", function(d) { return d.acc; }) .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); //node.append("title").text(function(d) { return d.className + ": " + format(d.value); }); node.append("circle") .attr("r", function(d) { return 30; }) .style("fill", function(d) { if(d.ratio == null) return "#ffffff"; else return color(d.ratio); }); node.append("text") .attr("dy", ".3em") .style("text-anchor", "middle") .text(function(d) { return d.acc; }); }); The resultant HTML has a ton of <g> tags responding to each element except they are never translated to the right position, but instead sort of sit on top of each other on the top left corner. By investigating in Firebug, I figured this happens presumably because the pack() algorithm does not get the objects one at a time, but the whole array as a single element, thus individual elements don't get .x and .y values. If I change the .nodes() argument to datagraph.children I get the elements one at a time in nodes() iteration, but oddly enough I get a single <g> object. Since I don't need to flatten a hierarchy I skipped the classes(root) function, in the example. What I am wondering is whether or not the packageName attribute plays any role in the nodes()? How can I resolve this issue?
You haven't specified a value accessor: var bubble = d3.layout.pack() .sort(null) .size([diameter, diameter]) .padding(1.5) .value(function(d) { return d.pval; }) //<- must return a number Example here.
Add images to d3 chord diagram
I would like images as the endpoints. I have tried adding but no luck. Any ideas/working examples? http://bost.ocks.org/mike/uberdata/
Each neighborhood in that example is given a <g> element with a class of group. // Add a group per neighborhood. var group = svg.selectAll(".group") .data(layout.groups) .enter().append("g") .attr("class", "group") .on("mouseover", mouseover); This is the element to which the text label and the endpoint path are appended. // Add the group arc. var groupPath = group.append("path") .attr("id", function(d, i) { return "group" + i; }) .attr("d", arc) .style("fill", function(d, i) { return cities[i].color; }); // Add a text label. var groupText = group.append("text") .attr("x", 6) .attr("dy", 15); You could append each image to this group also, using an svg <image> element. If, for example, your dataset contains the urls for your images, you might do the following: var groupImage = group.append("image") .attr("xlink:href", function(d) {return d.image_url;})
Using arc.Centroid to put an svg:image in the middle of the arc - but not appearing
I am currently trying to place a svg:image in the centre of my arc: var arcs = svg.selectAll("path"); arcs.append("svg:image") .attr("xlink:href", "http://www.e-pint.com/epint.jpg ") .attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; }) .attr("width", "150px") .attr("height", "200px"); I would appreciate it if someone could give me any advice on why it isn't appearing thanks : http://jsfiddle.net/xwZjN/17/
Looking at the jsfiddle, you are creating the path elements after you try to append the svg:image elements to the them. It should be the other way around. You should first create the arcs and then append the images. Second, as far as I know, the svg:path element should not contain any svg:image tags. It doesn't seem to display them if you place some inside. Instead what you should do is create svg:g tags with class arc and then use those to place the svg:images Slightly modifying your jsfiddle could look something like this: var colours = ['#909090','#A8A8A8','#B8B8B8','#D0D0D0','#E8E8E8']; var arcs = svg.selectAll("path"); for (var z=0; z<30; z++){ arcs.data(donut(data1)) .enter() //append the groups .append("svg:g") .attr("class", "arc") .append("svg:path") .attr("fill", function(d, i) { return colours[(Math.floor(z/6))]; }) .attr("d", arc[z]) .attr("stroke","black") } //here we append images into arc groups var pics = svg.selectAll(".arc").append("svg:image") .attr("xlink:href", "http://www.e-pint.com/epint.jpg ") .attr("transform", function(d,i) { //since you have an array of arc generators I used i to find the arc return "translate(" + arc[i].centroid(d) + ")"; }) .attr("x",-5) .attr("y",-10) .attr("width", "10px") .attr("height", "20px"); Where I also decreased the size of the images and offset them so that they fit into the arc.