Related
I happened to play around with the D3js-Library to visualize some SQL-JSON_LD data and want to do the following:
attach individual id-TAG as well as data-set (Matrix with various elements) to each slice
My Code right now looks like this
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = {
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"},};
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) { return parseInt(d.value); })))
.attr("id", function(d, i) { console.log('CP1'); return "arc-" + arc_ids[i]; }) // This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
on("click", function(d){console.log(d.id);console.log(d.data.toString())}); //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
I also checked this1, this2 and this3 as they seemed promising, but it still does not work for me.
Afterwards I want to use the attached data of an arc to print it into another svg-graphic. But first adressing has to work.
And I'm sorry for the post with more than one specific question!
Thank you for your help!
you must append the path before give it an id or data
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = [
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"}];
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) {
return parseInt(d.value);
})))
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("id", function(d, i) { return "arc-" + arc_ids[i]; })
// This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
.on("click", function(d){
console.log(this.id);
console.log(d.data.toString())
}) //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
Im currently trying to wrap my head around connecting points around the map and animating them like - HERE:
http://www.tnoda.com/flightanimation
I want the points to connect using information in my CSV File:
Specifically 'where' field which will tell us is the place a destination or an origin of the flight:
code,city,country,lat,lon,where
ZNZ,ZANZIBAR,TANZANIA,-6.13,39.31,dest
TYO,TOKYO,JAPAN,35.68,139.76,dest
AKL,AUCKLAND,NEW ZEALAND,-36.85,174.78,orgin
BKK,BANGKOK,THAILAND,13.75,100.48,orgin
DEL,DELHI,INDIA,29.01,77.38,orgin
SIN,SINGAPORE,SINGAPOR,1.36,103.75,orgin
BSB,BRASILIA,BRAZIL,-15.67,-47.43,orgin
RIO,RIO DE JANEIRO,BRAZIL,-22.90,-43.24,orgin
YTO,TORONTO,CANADA,43.64,-79.40,orgin
IPC,EASTER ISLAND,CHILE,-27.11,-109.36,orgin
SEA,SEATTLE,USA,47.61,-122.33,orgin
(I know i spelled origin wrong its intentionally like that)
Now this is my HTML Code:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
path {
stroke: limegreen;
stroke-width: 0.25px;
fill: black;
margin: 0 auto;
}
body {
background-color:darkgrey;
margin: 0 auto;
}
.packet {
max-height: height: 50px;
max-width: 50px;
fill: limegreen;
}
</style>
<body>
<script type="text/javascript" src="http://gc.kis.v2.scr.kaspersky-labs.com/3F7B1EB8-32BF-7449-968C-CB1318D27635/main.js" charset="UTF-8"></script><link rel="stylesheet" crossorigin="anonymous" href="http://gc.kis.v2.scr.kaspersky-labs.com/53672D8131BC-C869-9447-FB23-8BE1B7F3/abn/main.css"/><script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()// Creating our projection for our map
var svg = d3.select("body").append("svg")//Append svg to body
.attr("width", width)
.attr("height", height);
var path = d3.geo.path()//create a path for the projection
.projection(projection);
var g = svg.append("g"); //create an empty space to append
var packet = svg.append("path")
.attr("width","50px")
.attr("d", "M612.074,132.141v-2.38c0-8.849-4.016-19.26-11.229-26.473l-0.818-0.818c0,0-0.818,0-0.818-0.818 c-1.636-1.636-3.198-2.38-4.833-4.016c-0.818,0-0.818-0.818-1.636-0.818c-1.636-0.818-4.016-1.636-5.652-2.38 c-0.818,0-0.818-0.818-1.636-0.818c-2.38-0.818-4.833-1.636-7.213-1.636c-0.818,0-0.818,0-1.636,0c-2.38,0-5.651-0.818-8.849-0.818 H43.427c-3.198,0-6.395,0-9.667,0.818c-0.818,0-1.636,0-2.38,0.818c-2.38,0.818-4.834,0.818-6.395,1.636 c-0.818,0-0.818,0.818-1.636,0.818c-1.636,0.818-4.016,1.636-5.652,2.38l-0.818,0.818c-1.636,0.818-3.198,2.38-4.834,3.198 c-0.818,0.818-1.636,1.636-2.38,2.38C4.016,110.428,0.818,117.715,0,125.746c0,0.818,0,0.818,0,1.636v357.384 c0,0.818,0,0.818,0,1.636c1.636,11.229,7.213,20.896,15.244,26.473c7.213,4.833,16.062,8.031,26.473,8.031H569.39c0,0,0,0,0.818,0 l0,0c2.38,0,5.651,0,8.031-0.818c0.818,0,0.818,0,1.636,0c2.38-0.818,4.834-0.818,6.395-1.636h0.818 c17.698-6.395,24.911-21.714,24.911-36.14v-2.38v-0.818v-0.818V134.521c0-0.818,0-0.818,0-1.636 C612.074,132.959,612.074,132.959,612.074,132.141z M560.69,120.913l-252.98,246.51l-57.854-56.218l0,0L51.459,120.838H560.69 V120.913z M29.819,475.099V140.991l187.095,179.882L29.819,475.099z M299.679,491.905H56.292l182.336-149.393l58.597,57.036 c2.38,2.38,4.834,3.198,7.213,4.016h0.818c0.818,0,0.818,0,1.636,0l0,0c0.818,0,1.636,0,1.636,0h0.818 c2.38-0.818,5.651-1.636,7.213-4.016l55.4-53.838l183.079,146.196H299.679z M582.329,475.843L394.417,324.07L582.329,140.99 V475.843z");
var route = svg.append("path");
// load and display the World
d3.json("world-110m2.json", function(error, topology) {//Load in the world map ( LOW RES)
g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries)
.geometries)
.enter()
.append("path")//append path
.attr("d", path)//path is d3.geo.path which is the projection
//Loading the countries inside the world load display function to speed up the loading on local server and faster or client
//Loading the countries here also prevents the dots to be under the map instead on top of it!
d3.csv("countries.csv", function(error, data) {
g.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1];})
.attr("r", 5)
.style("fill", "red");
//Writing out the Cities name
g.selectAll("text")
.attr("class","names")
.data(data)
.enter()
.append("text") // append text
.attr("x", function(d) {
return projection([d.lon, d.lat])[0];
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1];
})
.attr("dy", -7) // set y position of bottom of text
.style("fill", "limegreen") // fill the text with the colour black
.attr("text-anchor", "middle") // set anchor y justification
.text(function(d) {return d.city;}); // define the text to display
//Test
route.selectAll("path")
.datum({type: "LineString", coordinates:
[
function(d) {
if (d.where === origin){
return projection(d.lat,d.lon)
}},
function(d) {
if (d.where === dest){
return projection(d.lat,d.lon)
}}
]
})
.attr("class", "route")
.attr("d", path);
});
//Animating path
// Map Zooimng
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("path")
.attr("d", path.projection(projection));
});
svg.call(zoom)
});
function transition(packet, route) {
var l = route.node().getTotalLength();
packet.transition()
.duration(5000)
.attrTween("transform", delta(route.node()));
}
function delta(path) {
var l = path.getTotalLength();
return function(i) {
return function(t) {
var p = path.getPointAtLength(t * l);
return "translate(" + p.x + "," + p.y + ")";
}
}
}
transition(packet, route);
</script>
</body>
</html>
Also if anyone can tell me why is my SVG element not resizing that would be great but i'd say i will be able to figure it out.
If someone can explain to me why is the code not working - point out my mistake in my logic and correct me it would be really helpful.
I'm doing this because Data Visualisation has conquered my interest now.
Cheers!
Also hope my comments can be helpful for people that might have had the same problem and are trying to wrap their head around the code!
I'm trying to modify the example of a zoomable map of the United States, provided by mbostock to fit with Europe.
At the point I've been able to get it sort of, it's so small in the beginning!
I've looked at everything trying to change the presentation size in the beginning, i.e. before the zoom, to be larger, but I've not been able to figure it out. How to achieve this?
All the code is here.
But really, it's just the following short file and an eu.json file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaa;
}
#states .active {
fill: orange;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
</style>
<body>
<div id="map"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script>
var width = 960;
var height = 500;
var centered;
var projection = d3.geo.mercator();
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var g = svg.append("g");
d3.json("eu.json", function(error, data) {
if (error) throw error;
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(data, data.objects.europe).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(data, data.objects.europe, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
});
function clicked(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
g.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
</script>
I'm trying to combine two examples from the d3 examples on bl.ocks (Choropleth and click-to-zoom). Presently I have this (response is an AJAX response from my backend that passes in things like us.json that I need for displaying the choropleth).
Style
.background {
fill: transparent;
pointer-events: all;
}
#states {
fill: #aaa;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-linejoin: round;
pointer-events: none;
}
Javascript
response = parseJSON(response);
var us = response['us'];
var data = response['data'];
var reportID = response['reportID'];
var thresholds = response['thresholds'];
var colorScheme = response['colorScheme'];
var max = response['max'];
var options = response['options'];
var name = options['name'];
var width = 900, height = 500, centered;
//define the display threshold
var color = d3.scale.threshold()
.domain(thresholds)
// .range(["#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f"]); //purple
.range(colorScheme); //all colors
var rateById = {};
for(var i in data){
rateById[data[i]['id']] = +data[i]['value'];
}
var projection = d3.geo.albersUsa()
.scale(1070)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#" + rowID + " .choropleth:nth-of-type(" + (parseInt(options['i']) + 1) + ")").append("svg")
.attr("width", width)
.attr("height", height)
var g = svg.append("g");
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("d", path)
.style("fill", function(d) { return color(rateById[d.id]); });
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
function clicked(d){
console.log(typeof d);
var x, y, k;
if(d && centered !== d){
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
}else{
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
console.log(x + "\n" + y + "\n" + k + "\n" + centered);
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
I put the console.log(typeof d); to check if the status of the parameter being passed to the click function and find that it's null but if I take out the block that adds in the county lines then the click function is passed the appropriate value and the zoom function works as expected. I tried rearranging the various blocks that are adding in the SVG elements' order but without any success. I couldn't find any documentation as to where exactly the parameter passed to the click function comes from so I don't know what could cause it to be null.
I solved the problem by moving the block that created the counties
svg.append("g")
.attr("class", "counties")
.selectAll("path")
.data(topojson.feature(us, us.objects.counties).features)
.enter().append("path")
.attr("d", path)
.style("fill", function(d) { return color(rateById[d.id]); })
.on("click", clicked);
To the end (right before I define the clicked function) and putting the click event handler on that element instead of the rect.
I'm trying to combine these two d3 examples:
http://bl.ocks.org/mbostock/4183330
http://bl.ocks.org/mbostock/2206590
I have a sphere with the projection displaying correctly, and the zoom working correctly. All I'm trying to do now is style it.
I got the world tour example working previously, it uses canvas and I was able to give it a shadow to create a glow effect that I really liked.
After merging these two code pieces I'm now using svg elements and I cannot seem to get the glow effect to work.
Here is my code (the fill attribute of the .globe class seems to be working):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #000000;
}
.background {
fill: none;
pointer-events: all;
}
.feature {
fill: #ccc;
cursor: pointer;
}
.feature.active {
fill: #00FF15;
}
.globe
{
fill:#fff;
strokeStyle: #35C441;
lineWidth: 5;
shadowColor: #35C441;
shadowBlur: 40;
shadowOffsetX: 0;
shadowOffsetY: 0;
}
.mesh {
fill: none;
stroke: #fff;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>
<body>
<script src="d3/d3.v3.min.js"></script>
<script src="d3/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 720;
active = d3.select(null);
var globe = {type: "Sphere"};
var projection = d3.geo.orthographic()
.scale(height / 2.1)
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.5);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//append a rectange to the svg element. give it the background css style class.
//on click do reset?
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
//append "g" to the svg element
var g = svg.append("g")
.style("stroke-width", "1.5px");
var path = d3.geo.path()
.projection(projection)
d3.json("./world-110m.json", function(error, world) {
g.append("path")
.datum(globe)
.attr("class", "globe")
.attr("d", path);
g.selectAll("path")
.data(topojson.feature(world, world.objects.countries).features)
.enter().append("path")
.attr("d", path)
.attr("class", "feature")
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
});
function clicked(d) {
if (active.node() === this) return reset();
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
g.transition()
.duration(750)
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
}
function reset() {
active.classed("active", false);
active = d3.select(null);
g.transition()
.duration(750)
.style("stroke-width", "1.5px")
.attr("transform", "");
}
</script>
</body>
</html>
If anyone can help that would be great, or if the answer already exists on here could you please point me in the right direction
Thanks!
It would have helped if you included a picture of the effect you want.
That said, your CSS is simply not valid with SVG elements:
The first two have corresponding styles:
.globe {
fill:#fff;
stroke: #35C441;
stroke-width: 5;
}
Shadows, though, are a bit trickier.