glowing sphere - d3 orthographic projection cant get css to work - javascript

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.

Related

Axis-range-slider alignment

I need to design a d3 component like the one shown in the figure below.
I referred to an existing code sample from this link, and modified it to create something like this.
Left was changing the width of the axis, which I tried by changing the stroke-width property of the domain class. However, I ended with something like this.
Problems:
The slider handle isn't aligning with the axis.
The axis color imprints on the slider.
The ends of the axis are not perfectly round.
Questions:
I can't figure out what do I translate/transform to align the sliders and the axis.
I tried fiddling around with the opacity values, but didn't help.
I set stroke-linecap to round, but it's still not completely round.
I am using d3 v4 for this. And the jsfiddle for my final code is here.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.tick{
visibility:hidden;
}
.domain {
stroke: grey;
stroke-width:10px;
stroke-linecap: round;
}
.selection {
fill:red
}
</style>
</head>
<body>
<div style="margin-left: 20px;margin-top: 20px;">
<span></span> to <span></span>
</div>
<script>
var margin = 20,
width = 400 - margin * 2,
height = 15;
// v3 = var x = d3.scale.linear()
var x = d3.scaleLinear()
.domain([0,100])
.range([0, width]);
/*
var brush = d3.svg.brush()
.x(x)
.extent([20, 50]);
*/
var brush = d3.brushX()
.extent([[0,0], [width,height]])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin * 2)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
.call(d3.axisBottom()
.scale(x)
.tickSize(0));
var brushg = svg.append("g")
.attr("class", "brush")
.call(brush)
// left circle
var left_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
var right_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
/*
Height of the brush's rect is now
generated by brush.extent():
brushg.selectAll("rect")
.attr("height", height);
*/
function brushed() {
/*
The brush attributes are no longer stored
in the brush itself, but rather in the
element it is brushing. That's where much of
the confusion around v4's brushes seems to be.
The new method is a little difficult to adapt
to, but seems more efficient. I think much of
this confusion comes from the fact that
brush.extent() still exists, but means
something completely different.
Instead of calling brush.extent() to get the
range of the brush, call
d3.brushSelection(node) on what is being
brushed.
d3.select('#start-number')
.text(Math.round(brush.extent()[0]));
d3.select('#end-number')
.text(Math.round(brush.extent()[1]));
*/
var range = d3.brushSelection(this)
.map(x.invert);
console.log('range->'+range)
d3.selectAll("span")
.text(function(d, i) {
console.log(Math.round(range[i]))
return Math.round(range[i])
})
left_text.attr("x", x(range[0]));
left_text.text(Math.round(range[0]));
right_text.attr("x", x(range[1]));
right_text.text(Math.round(range[1]));
d3.selectAll("rect").attr("dy", "-5em")
}
// v3: brushed();
brush.move(brushg, [20, 40].map(x));
</script>
</body>
</html>
The axis and the brush are actually perfectly aligned!
You can see this if you set the stroke-width to 1px:
.as-console-wrapper { max-height: 30% !important;}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.tick{
visibility:hidden;
}
.domain {
stroke: grey;
stroke-width:1px;
stroke-linecap: round;
}
.selection {
fill:red
}
</style>
</head>
<body>
<div style="margin-left: 20px;margin-top: 20px;">
<span></span> to <span></span>
</div>
<script>
var margin = 20,
width = 400 - margin * 2,
height = 15;
// v3 = var x = d3.scale.linear()
var x = d3.scaleLinear()
.domain([0,100])
.range([0, width]);
/*
var brush = d3.svg.brush()
.x(x)
.extent([20, 50]);
*/
var brush = d3.brushX()
.extent([[0,0], [width,height]])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin * 2)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
.call(d3.axisBottom()
.scale(x)
.tickSize(0));
var brushg = svg.append("g")
.attr("class", "brush")
.call(brush)
// left circle
var left_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
var right_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
/*
Height of the brush's rect is now
generated by brush.extent():
brushg.selectAll("rect")
.attr("height", height);
*/
function brushed() {
/*
The brush attributes are no longer stored
in the brush itself, but rather in the
element it is brushing. That's where much of
the confusion around v4's brushes seems to be.
The new method is a little difficult to adapt
to, but seems more efficient. I think much of
this confusion comes from the fact that
brush.extent() still exists, but means
something completely different.
Instead of calling brush.extent() to get the
range of the brush, call
d3.brushSelection(node) on what is being
brushed.
d3.select('#start-number')
.text(Math.round(brush.extent()[0]));
d3.select('#end-number')
.text(Math.round(brush.extent()[1]));
*/
var range = d3.brushSelection(this)
.map(x.invert);
console.log('range->'+range)
d3.selectAll("span")
.text(function(d, i) {
console.log(Math.round(range[i]))
return Math.round(range[i])
})
left_text.attr("x", x(range[0]));
left_text.text(Math.round(range[0]));
right_text.attr("x", x(range[1]));
right_text.text(Math.round(range[1]));
d3.selectAll("rect").attr("dy", "-5em")
}
// v3: brushed();
brush.move(brushg, [20, 40].map(x));
</script>
</body>
</html>
So, what's happening here? The issue is that when you tell the browser to take a line (in this case it's a path, but it doesn't matter) and increase its stroke to, let's say, 100 pixels, it will increase 50 pixels to one side and 50 pixels to the other side. So, the middle of that thick axis is right on the top of the brush's rectangle.
There are several solutions here, like drawing an rectangle. If, however, you want to keep your approach of increasing the .domain stroke-width, let's break the selections and move the axis half its stroke-width down (here I'm increasing the width to 20 pixels, so it's easier to see the alignment):
.as-console-wrapper { max-height: 30% !important;}
<!DOCTYPE html>
<meta charset="utf-8">
<script src="//d3js.org/d3.v4.min.js"></script>
<!--
axes and brushes are styled out of the box,
so this is no longer needed
<style>
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
fill-opacity: .125;
shape-rendering: crispEdges;
}
</style>
-->
<style>
.tick {
visibility: hidden;
}
.domain {
stroke: grey;
stroke-width: 20px;
stroke-linecap: round;
}
.selection {
fill: red
}
</style>
<body>
<div style="margin-left: 20px;margin-top: 20px;">
<span></span> to <span></span>
</div>
</body>
<script>
var margin = 20,
width = 400 - margin * 2,
height = 20;
// v3 = var x = d3.scale.linear()
var x = d3.scaleLinear()
.domain([0, 100])
.range([0, width]);
/*
var brush = d3.svg.brush()
.x(x)
.extent([20, 50]);
*/
var brush = d3.brushX()
.extent([
[0, 0],
[width, height]
])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin * 2)
.attr("height", 100);
svg.append("g")
.attr("transform", "translate(" + margin + "," + (margin + 10) + ")")
.call(d3.axisBottom()
.scale(x)
.tickSize(0));
var brushg = svg.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
.attr("class", "brush")
.call(brush)
// left circle
var left_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
var right_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
/*
Height of the brush's rect is now
generated by brush.extent():
brushg.selectAll("rect")
.attr("height", height);
*/
function brushed() {
/*
The brush attributes are no longer stored
in the brush itself, but rather in the
element it is brushing. That's where much of
the confusion around v4's brushes seems to be.
The new method is a little difficult to adapt
to, but seems more efficient. I think much of
this confusion comes from the fact that
brush.extent() still exists, but means
something completely different.
Instead of calling brush.extent() to get the
range of the brush, call
d3.brushSelection(node) on what is being
brushed.
d3.select('#start-number')
.text(Math.round(brush.extent()[0]));
d3.select('#end-number')
.text(Math.round(brush.extent()[1]));
*/
var range = d3.brushSelection(this)
.map(x.invert);
console.log('range->' + range)
d3.selectAll("span")
.text(function(d, i) {
console.log(Math.round(range[i]))
return Math.round(range[i])
})
left_text.attr("x", x(range[0]));
left_text.text(Math.round(range[0]));
right_text.attr("x", x(range[1]));
right_text.text(Math.round(range[1]));
d3.selectAll("rect").attr("dy", "-5em")
}
// v3: brushed();
brush.move(brushg, [20, 40].map(x));
</script>
The path in the axis is a closed shape and stroking that gives problems. Also you don't want ticks so why not draw the "axis" yourself. Then the round edge will be drawn correct.
var svg = d3.select("body").append("svg")
.attr("width", width + margin * 2)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
// .call(d3.axisBottom()
// .scale(x)
// .tickSize(0))
;
svg.append("path")
.attr("class", "domain")
.attr("d", `M${x(0)},0 ${x(100)},0`);
You have to match the brush extent to the stroked path surface
var margin = 20,
width = 400 - margin * 2,
height = 10; // same as stroke width
var brush = d3.brushX()
.extent([[0,-height*0.5], [width,height*0.5]])
.on("brush", brushed);
The dy attribute has no purpose
//d3.selectAll("rect").attr("dy", "-5em")
Set the fill-opacity of the selection
.selection {
fill:red;
fill-opacity: 1;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.tick{
visibility:hidden;
}
.domain {
stroke: grey;
stroke-width:10;
stroke-linecap: round;
}
.selection {
fill:red;
fill-opacity: 1;
}
</style>
</head>
<body>
<div style="margin-left: 20px;margin-top: 20px;">
<span></span> to <span></span>
</div>
<script>
var margin = 20,
width = 400 - margin * 2,
height = 10; // same as stroke width
// v3 = var x = d3.scale.linear()
var x = d3.scaleLinear()
.domain([0,100])
.range([0, width]);
/*
var brush = d3.svg.brush()
.x(x)
.extent([20, 50]);
*/
var brush = d3.brushX()
.extent([[0,-height*0.5], [width,height*0.5]])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin * 2)
.attr("height", 100)
.append("g")
.attr("transform", "translate(" + margin + "," + margin + ")")
// .call(d3.axisBottom()
// .scale(x)
// .tickSize(0))
;
svg.append("path")
.attr("class", "domain")
.attr("d", `M${x(0)},0 ${x(100)},0`);
var brushg = svg.append("g")
.attr("class", "brush")
.call(brush)
// left circle
var left_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
var right_text = brushg.append("text")
.attr("class", "label")
.attr("fill", "black")
.attr("text-anchor", "middle")
.text("hello world")
.attr("transform", "translate(0," + (35) + ")")
/*
Height of the brush's rect is now
generated by brush.extent():
brushg.selectAll("rect")
.attr("height", height);
*/
function brushed() {
/*
The brush attributes are no longer stored
in the brush itself, but rather in the
element it is brushing. That's where much of
the confusion around v4's brushes seems to be.
The new method is a little difficult to adapt
to, but seems more efficient. I think much of
this confusion comes from the fact that
brush.extent() still exists, but means
something completely different.
Instead of calling brush.extent() to get the
range of the brush, call
d3.brushSelection(node) on what is being
brushed.
d3.select('#start-number')
.text(Math.round(brush.extent()[0]));
d3.select('#end-number')
.text(Math.round(brush.extent()[1]));
*/
var range = d3.brushSelection(this)
.map(x.invert);
//console.log('range->'+range)
d3.selectAll("span")
.text(function(d, i) {
//console.log(Math.round(range[i]))
return Math.round(range[i])
})
left_text.attr("x", x(range[0]));
left_text.text(Math.round(range[0]));
right_text.attr("x", x(range[1]));
right_text.text(Math.round(range[1]));
//d3.selectAll("rect").attr("dy", "-5em")
}
// v3: brushed();
brush.move(brushg, [20, 40].map(x));
</script>
</body>
</html>

Add <id> + <data> to an ARC of a D3-PIE-chart

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>

Implement d3 Click-to-zoom

I'm new to web stuff but even I know this is a stupid question... I still can't figure out what I'm doing wrong though. The code on the site here: http://bl.ocks.org/mbostock/2206590 seems as if you could copy and paste it into an html document and merely modify the link to the us.json so that it points to the full file path. However, the code merely pulls up a blank page.
The inspection of the page source code on the demo ( http://bl.ocks.org/mbostock/raw/2206590/ )is the exact same as the code provided on the main page. What am I missing to implement this??
Thanks!!!
<!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: 1.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500,
centered;
var projection = d3.geo.albersUsa()
.scale(1070)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").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("/mbostock/raw/4090846/us.json", function(error, us) {
if (error) throw error;
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, 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>
Download the us.json file and put it in the same directory as your index.html. Then change the path to the us.json file:
d3.json("us.json", function(error, us) {
This worked for me. If you are using Chrome and accessing the index.html file on your local computer, not from a remote webserver, you will need to run a local webserver for this to work. If you just try to open the index.html file in Chrome, it won't display properly due to Chrome's local file restrictions. It needs to be accessed through a webserver.
Hope this helps.

d3.js graticule extent not responsive

I am trying to plot a png map file with graticules. The graticule extent should correspond to the width and height of the map file (see end of script). Although the left and upper extent show correctly, the lower and right extent do not correspond to the map dimensions.
I also played around with the extent values in the map function and only the left and upper extent are responsive.
Any suggestions?
<!doctype html>
<meta charset="utf-8">
<script src="./js/d3.js"></script>
<script src="./js/topojson.js"></script>
<script src="./js/jquery3.1.0.min.js"></script>
<style>
.MapPad {
padding: 30px 30px 30px 30px;
}
.graticule {
fill: none;
stroke: #000;
stroke-opacity: .15;
}
.graticule.outline {
stroke: black;
stroke-opacity: 1;
stroke-width: 2px;
stroke-dasharray: initial;
}
.LonLatLabel {
font-family: helvetica;
font-size: 22px;
dominant-baseline: central;
text-anchor: middle;
</style>
<body>
<div id='cont1_1'></div>
<script charset="utf-8">
//The function to plot the maps
function plotMaps (container, width, height, rasterBounds, demFile){
var projection = d3.geoMercator()
.scale(1)
.translate([0, 0]);
var b = [projection(rasterBounds[0]), projection(rasterBounds[1])],
s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]
//update projection
projection
.scale(s)
.translate(t)
// geo path generator
var path = d3.geoPath()
.projection(projection)
var map = d3.select(container).append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'MapPad');
//define the data layers before drawing to ensure the order of appearance
var gratLines = map.append('g');
var demLayer = map.append('g');
var samplPointsLayer = map.append('g');
var outline = map.append('g');
//make the graticule
var graticule = d3.geoGraticule().extent([[rasterBounds[0][0], rasterBounds[1][0]], [rasterBounds[0][1], rasterBounds[1][1]]]).step([1, 1]);
gratLines.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// get the coordinates of the line paths and use them as labels
map.selectAll('text')
.data(graticule.lines())
.enter().append("text")
.text(function(d) {
if (d.coordinates[0][0] == d.coordinates[1][0]) {return (d.coordinates[0][0]);}
else if (d.coordinates[0][1] == d.coordinates[1][1]) {return (d.coordinates[0][1]);}
})
.attr("class","LonLatLabel")
.attr('transform', function(d) { return ('translate(' + projection(d.coordinates[0])[0] + ',' + projection(d.coordinates[1])[1] + ')')
});
//outline of the map
outline.append("path")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", path);
/*var color = d3.scale.ordinal()
.domain(["1", "2", "3"])
.range(["#ffd633", "#aaff00" , "#267300"]);
*/
demLayer.append('svg:image')
.attr('xlink:href', demFile)
.attr('width', width)
.attr('height', height);
d3.json('SamplingPoints.json', function(err, data) {
samplPointsLayer.selectAll('circles')
.data(data.features)
.enter().append('circle')
.attr('r', 5)
.each(function(d) {
var lonlat = projection(d.geometry.coordinates);
d3.select(this)
.attr('cx', lonlat[0])
.attr('cy', lonlat[1])
.style('fill', 'black')
.style("opacity", .5)
});
});
}
//calculate the number with which the size of each map should be divided
var mainWidth = 230
//Plot the maps in each div
//Alps
var widthAlps = 4665;
var heightAlps = 3589;
var resCoefAlps = widthAlps/mainWidth
var rasterBoundsAlps = [[ 5.907077970880465 , 45.29815864865324 ] , [ 11.330836684119511 , 48.15780097787413 ]];
plotMaps('#cont1_1', widthAlps/resCoefAlps, heightAlps/resCoefAlps, rasterBoundsAlps, 'dem_alps.png');
</script>
</body>
Here's the result:
enter image description here
Issue solved! The [xmin, ymin], [xmax, ymax] values in the d3.geoGraticule().extent() function should be:
[rasterBounds[0][0], rasterBounds[0][1]], [rasterBounds[1][0], rasterBounds[1][1]]]

d3.js zoomable map too small in the beginning

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>

Categories

Resources