Edge on interactive d3 visualization - javascript

I'm very new to d3 and I found this code for some visualization I'm working on.
I ommited most of the data to fit in this page more smoothly.
Basically, what I want to do is make the edges of the bars appear only for the selected variable when you click on it, right now, whenever you select one variable the edges for the others are shown so it looks horrible. Much help would be appreciated!
Thanks!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text{
font-size:12px;
}
.mainBars rect{
shape-rendering: auto;
fill-opacity: 0;
stroke-width: 0.5px;
stroke: rgb(0, 0, 0);
stroke-opacity: 0;
}
.subBars{
shape-rendering:crispEdges;
}
.edges{
stroke:black;
stroke-width: .4px:
fill-opacity:0.5;
}
.header{
text-anchor:middle;
font-size:18px;
font-weight: bold;
line-height: 1.5;
}
line{
stroke:red;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="http://vizjs.org/viz.v1.1.0.min.js"></script>
<script>
var data=[['CA', 'Wheat', 2441900000.0, 2441900000.0],
['CO', 'Alfalfa', 4410000000.0, 4410000000.0],
['KS', 'Sorghum', 13300000000.0, 13300000000.0],
['TX', 'Sorghum', 12500000000.0, 12500000000.0],
['WY', 'Safflower', 5655522.245, 5655522.245]];
//Create array below, one color per state.
//There's probably a programmatic way to use a d3 color palette
//to just automatically do it instead of listing 51 colors individually.
//e.g. d3.scale.category10(), also
// http://stackoverflow.com/questions/20847161/how-can-i-generate-as-many-colors-as-i-want-using-d3
var color ={KS:"#3366CC", WY:"#DC3912", CA:"#FF9900", CO:"#109618", TX:"#990099", IA:"#0099C6"};
var svg = d3.select("body").append("svg").attr("width", 960).attr("height", 800);
svg.append("text").attr("x",250).attr("y",70)
.attr("class","header").text("Crop Acreage by State - 2015");
//svg.append("text").attr("x",750).attr("y",70)
// .attr("class","header").text("Duplicate");
var g =[svg.append("g").attr("transform","translate(150,100)")
,svg.append("g").attr("transform","translate(650,100)")];
var bp=[ viz.bP()
.data(data)
.min(12)
.pad(0.5)
.height(600)
.width(300)
.barSize(35)
.fill(d=>color[d.primary])
// ,viz.bP()
// .data(data)
// .value(d=>d[3])
// .min(12)
// .pad(1)
// .height(600)
// .width(200)
// .barSize(35)
// .fill(d=>color[d.primary])
];
[0,1].forEach(function(i){
g[i].call(bp[i])
g[i].append("text").attr("x",-50).attr("y",-8).style("text-anchor","middle").text("State");
g[i].append("text").attr("x", 250).attr("y",-8).style("text-anchor","middle").text("Crop");
g[i].append("line").attr("x1",-100).attr("x2",0);
g[i].append("line").attr("x1",200).attr("x2",300);
g[i].append("line").attr("y1",700).attr("y2",700).attr("x1",-100).attr("x2",0);
g[i].append("line").attr("y1",610).attr("y2",610).attr("x1",200).attr("x2",300);
g[i].selectAll(".mainBars")
.on("mouseover",mouseover)
.on("mouseout",mouseout);
g[i].selectAll(".mainBars").append("text").attr("class","label")
.attr("x",d=>(d.part=="primary"? -30: 30))
.attr("y",d=>+6)
.text(d=>d.key)
.attr("text-anchor",d=>(d.part=="primary"? "end": "start"));
g[i].selectAll(".mainBars").append("text").attr("class","perc")
.attr("x",d=>(d.part=="primary"? -100: 100))
.attr("y",d=>+6)
.text(function(d){ return d3.format("0.0%")(d.percent)})
.attr("text-anchor",d=>(d.part=="primary"? "end": "start"));
});
function mouseover(d){
[0,1].forEach(function(i){
bp[i].mouseover(d);
g[i].selectAll(".mainBars").select(".perc")
.text(function(d){ return d3.format("0.0%")(d.percent)});
});
}
function mouseout(d){
[0,1].forEach(function(i){
bp[i].mouseout(d);
g[i].selectAll(".mainBars").select(".perc")
.text(function(d){ return d3.format("0.0%")(d.percent)});
});
}
d3.select(self.frameElement).style("height", "800px");
</script>
</body>
</html>

remove the .edges style
.edges{
stroke:black;
stroke-width: .4px;
fill-opacity:0.5;
}
viz animates the fill-opacity instead of opacity

Related

d3.js chart rendering at bottom of wordpress page

Why is my js chart rendering at the bottom of the page and not in the body? I've used tons of these within my site with no problems but this one doesn't seem to want to go where desired. Thoughts?
Thank you in advance!
www.wcsddata.net/data-topics/jttest/
<center><!DOCTYPE html>
<meta charset="utf-8">
<style>
text{
font-size:12px;
}
.mainBars rect{
shape-rendering: auto;
fill-opacity: 0;
stroke-width: 0.5px;
stroke: rgb(0, 0, 0);
stroke-opacity: 0;
}
.subBars{
shape-rendering:crispEdges;
}
.edges{
stroke:none;
fill-opacity:0.5;
}
.header{
text-anchor:middle;
font-size:16px;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="http://vizjs.org/viz.v1.1.0.min.js"></script>
<script>
var data=[['Lite','CA',16,0],
['Small','CA',1278,4],
['Medium','CA',27,0],
['Plus','CA',58,0],
['Grand','CA',1551,15],
['Elite','CA',141,0],
['Lite','AZ',5453,35],
['Small','AZ',683,1],
['Medium','AZ',862,0],
['Grand','AZ',6228,30],
['Lite','AL',15001,449],
['Small','AL',527,3],
['Medium','AL',836,0],
['Plus','AL',28648,1419],
['Grand','AL',3,0],
['Lite','CO',13,0],
['Small','CO',396,0],
['Medium','CO',362,0],
['Plus','CO',78,10],
['Grand','CO',2473,32],
['Elite','CO',2063,64],
['Medium','DE',203,0],
['Grand','DE',686,2],
['Elite','DE',826,0],
['Lite','KS',1738,110],
['Small','KS',12925,13],
['Medium','KS',15413,0],
['Small','GA',2166,2],
['Medium','GA',86,0],
['Plus','GA',348,3],
['Grand','GA',4244,18],
['Elite','GA',1536,1],
['Small','IA',351,0],
['Grand','IA',405,1],
['Small','IL',914,1],
['Medium','IL',127,0],
['Grand','IL',1470,7],
['Elite','IL',516,1],
['Lite','IN',43,0],
['Small','IN',667,1],
['Medium','IN',172,0],
['Plus','IN',149,1],
['Grand','IN',1380,5],
['Elite','IN',791,23],
['Small','FL',1,0],
['Grand','FL',1,0],
['Small','MD',1070,1],
['Grand','MD',1171,2],
['Elite','MD',33,0],
['Plus','TX',1,0],
['Small','MS',407,0],
['Medium','MS',3,0],
['Grand','MS',457,2],
['Elite','MS',20,0],
['Small','NC',557,0],
['Medium','NC',167,0],
['Plus','NC',95,1],
['Grand','NC',1090,5],
['Elite','NC',676,6],
['Lite','NM',1195,99],
['Small','NM',350,3],
['Medium','NM',212,0],
['Grand','NM',1509,8],
['Lite','NV',3899,389],
['Small','NV',147,0],
['Medium','NV',455,0],
['Plus','NV',1,1],
['Grand','NV',4100,16],
['Lite','OH',12,0],
['Small','OH',634,2],
['Medium','OH',749,0],
['Plus','OH',119,1],
['Grand','OH',3705,19],
['Elite','OH',3456,25],
['Small','PA',828,2],
['Medium','PA',288,0],
['Plus','PA',141,0],
['Grand','PA',2625,7],
['Elite','PA',1920,10],
['Small','SC',1146,2],
['Medium','SC',212,0],
['Plus','SC',223,4],
['Grand','SC',1803,6],
['Elite','SC',761,8],
['Small','TN',527,0],
['Medium','TN',90,0],
['Grand','TN',930,4],
['Elite','TN',395,1],
['Lite','ME',7232,58],
['Small','ME',1272,0],
['Medium','ME',1896,0],
['Plus','ME',1,0],
['Grand','ME',10782,33],
['Elite','ME',1911,3],
['Small','VA',495,0],
['Medium','VA',32,0],
['Plus','VA',7,0],
['Grand','VA',1557,12],
['Elite','VA',24,0],
['Small','WA',460,1],
['Plus','WA',88,3],
['Grand','WA',956,3],
['Small','WV',232,0],
['Medium','WV',71,0],
['Grand','WV',575,2],
['Elite','WV',368,3]
];
var color ={Elite:"#3366CC", Grand:"#DC3912", Lite:"#FF9900", Medium:"#109618", Plus:"#990099", Small:"#0099C6"};
var svg = d3.select("body").append("svg").attr("width", 960).attr("height", 800);
svg.append("text").attr("x",250).attr("y",70)
.attr("class","header").text("Sales Attempt");
svg.append("text").attr("x",750).attr("y",70)
.attr("class","header").text("Sales");
var g =[svg.append("g").attr("transform","translate(150,100)")
,svg.append("g").attr("transform","translate(650,100)")];
var bp=[ viz.bP()
.data(data)
.min(12)
.pad(1)
.height(600)
.width(200)
.barSize(35)
.fill(d=>color[d.primary])
,viz.bP()
.data(data)
.value(d=>d[3])
.min(12)
.pad(1)
.height(600)
.width(200)
.barSize(35)
.fill(d=>color[d.primary])
];
[0,1].forEach(function(i){
g[i].call(bp[i])
g[i].append("text").attr("x",-50).attr("y",-8).style("text-anchor","middle").text("Channel");
g[i].append("text").attr("x", 250).attr("y",-8).style("text-anchor","middle").text("State");
g[i].append("line").attr("x1",-100).attr("x2",0);
g[i].append("line").attr("x1",200).attr("x2",300);
g[i].append("line").attr("y1",610).attr("y2",610).attr("x1",-100).attr("x2",0);
g[i].append("line").attr("y1",610).attr("y2",610).attr("x1",200).attr("x2",300);
g[i].selectAll(".mainBars")
.on("mouseover",mouseover)
.on("mouseout",mouseout);
g[i].selectAll(".mainBars").append("text").attr("class","label")
.attr("x",d=>(d.part=="primary"? -30: 30))
.attr("y",d=>+6)
.text(d=>d.key)
.attr("text-anchor",d=>(d.part=="primary"? "end": "start"));
g[i].selectAll(".mainBars").append("text").attr("class","perc")
.attr("x",d=>(d.part=="primary"? -100: 80))
.attr("y",d=>+6)
.text(function(d){ return d3.format("0.0%")(d.percent)})
.attr("text-anchor",d=>(d.part=="primary"? "end": "start"));
});
function mouseover(d){
[0,1].forEach(function(i){
bp[i].mouseover(d);
g[i].selectAll(".mainBars").select(".perc")
.text(function(d){ return d3.format("0.0%")(d.percent)});
});
}
function mouseout(d){
[0,1].forEach(function(i){
bp[i].mouseout(d);
g[i].selectAll(".mainBars").select(".perc")
.text(function(d){ return d3.format("0.0%")(d.percent)});
});
}
d3.select(self.frameElement).style("height", "800px");
</script>
problem is your JS selector, you need to select #content.
change
var svg = d3.select("body").append("svg").attr("width", 960).attr("height", 800);
to:
var svg = d3.select("#content").append("svg").attr("width", 960).attr("height", 800);
this will show your charts in desired area.
in order to center the charts you need to change the width and height:
var svg = d3.select("#content").append("svg").attr("width", 600).attr("height", auto);
then create a
<div id="mycenterdiv" style="text-align:center; width: 100%;"> </div>
in your wordpress page and then target that div (change the select)
var svg = d3.select("#mycenterdiv").append("svg").attr("width", 600).attr("height", auto);

Incorrect color selection of regions in the map

I don't understand why the color of legend's labels does not always correspond to the color in the map in my code:
This is my code (that is the modification of this code). The file provincias.json is available here:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.nombre{
stroke: #000;
stroke-width: 0.5px
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.legendLinear
{
font-family: "Lato";
fill:#c2b59b;
}
.legendTitle {
font-size: 1em;
}
#tooltip {
position: absolute;
top: 0;
left: 0;
z-index: 10;
margin: 0;
padding: 10px;
width: 200px;
height: 70px;
color: white;
font-family: sans-serif;
font-size: 1.0em;
font-weight: bold;
text-align: center;
background-color: rgba(0, 0, 0, 0.55);
opacity: 0;
pointer-events: none;
border-radius:5px;
transition: .2s;
}
</style>
<body>
<div id="container">
<div id="tooltip">
</div>
</div>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-composite-projections/0.3.5/conicConformalSpain-proj.min.js"></script>
<script>
var width = 1000,
height = 900;
var projection = d3.geo.conicConformalSpain()
var graticule = d3.geo.graticule().step([2, 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#container").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("provincias.json", function(error, provincias) {
d3.json("hdi.json", function(error, hdi) {
var land = topojson.feature(provincias, provincias.objects.provincias);
var color = d3.scale.linear()
.domain([0, 10, 1000, 10000, 100000, 300000])
.range(["#feebe2","#e5d1ff","#ba93ef", "#8D4CE5","#6100E5","#C94D8C"]); //#feebe2
svg.selectAll(".nombre")
.data(land.features)
.enter()
.append("path")
.attr("d", path)
.attr("class","nombre")
.style("fill",function(d){ return color(hdi[d.properties.nombre]) })
.on("mouseover", function(d){
//Show the tooltip
var x = d3.event.pageX;
var y = d3.event.pageY - 40;
d3.select("#tooltip")
.style("left", x + "px")
.style("top", y + "px")
.style("opacity", 1)
.text(d.properties.nombre + "," + hdi[d.properties.nombre]);
})
.on("mouseout", function(){
//Hide the tooltip
d3.select("#tooltip")
.style("opacity", 0);
});
svg
.append("path")
.style("fill","none")
.style("stroke","#000")
.attr("d", projection.getCompositionBorders());
d3.select("svg").append("g")
.attr("class", "legendLinear")
.attr("transform", "translate(100,500)");
var legendLinear = d3.legend.color()
.title("...")
.shapeHeight(20)
.shapeWidth(90)
.shapeRadius(10)
.cells([0, 10, 1000, 10000, 100000, 300000])
.orient("horizontal")
.labelFormat(d3.format(".00f"))
.labelAlign("start")
.scale(color);
svg.select(".legendLinear")
.call(legendLinear);
});
});
</script>
The content of hdi.json is the following:
{"Coruña, A":9, "Alicante":158, "Albacete":3,"Almería":0,"Asturias":13,"Álava":12,"Ávila":0,
"Badajoz":10,"Balears, Illes":331,"Barcelona":250000,"Burgos":5,
"Cantabria":12,"Castellón":316,"Ceuta":9,"Ciudad Real":9,"Cádiz":9,"Cuenca":4,
"Córdoba":11,"Cáceres":2,"Girona":21808,"Jaén":0,
"Granada":9,"Huelva":3,"Huesca":74,
"León":5,"Lleida":9672,"Lugo":3,
"Madrid":507,"Murcia":24,"Málaga":25,"Palencia":2,"Pontevedra":6,
"Navarra":23,"Salamanca":6,"Segovia":4,"Sevilla":16,"Soria":2,
"Santa Cruz de Tenerife":16,"Tarragona":22790,
"Teruel":23,"Toledo":4,"Valladolid":44,
"Valencia":423,"Vizcaya":19,"Zamora":0,"Zaragoza":56,"Guipúzcoa":21,
"Guadalajara":5,"Jaen":2,"Rioja, La": 12, "Palmas, Las": 10,"Ourense":2}
The particular problem is that Tarragona that has the value 22790 is colored in the same color as Lleida that has the value 9672. However, according to my code, 22790 (Tarragona) is smaller than 100000 and bigger than 10000, so it should be colored in #6100E5, but it's colored in #8D4CE5.
But, for example, 9672 (Lleida) is smaller than 10000, so it should be colored in #8D4CE5 (and it is colored in this color, so it's ok).
You shouldn't be using a linear scale, by definition it has a continuous range and will interpolate between the colors. What you are describing is a threshold scale. It has a discrete range mapped to subsets of domain values. Further, you must call it with a range that's N + 1 of the domain, so, this is what you should be after:
var color = d3.scale.threshold()
.domain([10, 1000, 10000, 100000, 30000])
.range(["#feebe2","#e5d1ff","#ba93ef", "#8D4CE5","#6100E5","#C94D8C"]);
Here's an example creating the legend using a threshold scale and d3-legend.
<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.7.0/d3-legend.min.js"></script>
</head>
<body>
<svg></svg>
<script>
var color = d3.scale.threshold()
.domain([10, 1000, 10000, 100000, 300000])
.range(["#feebe2", "#e5d1ff", "#ba93ef", "#8D4CE5", "#6100E5", "#C94D8C"]);
var svg = d3.select("svg");
svg.append("g")
.attr("class", "legendLog")
.attr("transform", "translate(20,20)");
var logLegend = d3.legend.color()
.labels([0, 10, 1000, 10000, 100000, 300000])
.scale(color);
svg.select(".legendLog")
.call(logLegend);
</script>
</body>
</html>
Actually it works as expected.
The exact colors of Lleida and Tarragona are different, the former being #8f4fe5, the latter #8741e5.
Because of the linear scales you use these colors are calculated as:
((9672-1000) * #8d4ce5 + (10000-9672) * #ba93ef)/(10000-1000) = #8f4fe5
((22790-10000) * #6100e5 + (100000-22790) * #8d4ce5)/(100000-10000) = #8741e5
Intuitively, your problem is that 9672 is much closer to 10000 as to 1000, and 22790 is also much closer to 10000 than to 100000, so even if one of them is below 10000, and the other one above it, they are still closer to it, than to the other ends of the ranges.
As Mark suggested in his answer, probably you do not want to use continuous linear scales.

relocate d3 map to center of screen no matter the device

I've got the following d3 map:
but as you can see it's all bunched up there in the left corner of the screen- this is sub opimal- ideally I would like it to be centered.
How can I achieve this?
I guess it should be easy but every time I google something like "center align d3 map" I get things about zooming
:/
maybe I need to create a div or something?
the code is here on my GitHub
also below- it's pretty well commented.
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<style>
.border {
stroke: #000;
fill: none;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
div.tooltip {
position: absolute;
text-align: center;
width: 84px;
height: 64px;
padding: 2px;
font: 12px sans-serif;
background: lightgrey;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<h1>Administrative Sub-Regions of Europe</h1>
<!-- this is the form at the bottom to change the NUTS -->
<form>
<select id="json_sources" name="json_sources" >
<option value ="nuts0" selected >Source 0</option>
<option value ="nuts1" >Source 1</option>
<option value ="nuts2" >Source 2</option>
<option value ="nuts3" >Source 3</option>
</select>
<form>
<!-- spinner -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.0.1/spin.min.js'></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="http://d3js.org/colorbrewer.v1.min.js"></script>
<script src="https://cdn.rawgit.com/rveciana/d3-composite-projections/v0.2.0/composite-projections.min.js"></script>
<!-- why do we need this? -->
<section id='chart'>
</section>
<script>
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var width = 600,
height = 500;
var projection = d3.geo.conicConformalEurope();
var graticule = d3.geo.graticule();
var path = d3.geo.path()
.projection(projection);
// Find new colours here: http://colorbrewer2.org/
var scale = d3.scale
.quantize()
.domain([10,60])
.range(colorbrewer.PuRd[3]);
var svg = d3.select("body")
.append("svg")
.attr("width", width, "100%")
.attr("height", height, "100%")
.call(
d3.
behavior.
zoom().
on("zoom", function () {
svg.attr("transform", "translate(" +
d3.event.translate +
")" + " scale(" +
d3.event.scale +
")")
}
)
)
.on("dblclick.zoom", null)
.append("g")
//what the hell does this do?
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// pretty self spoken
var dropdown = d3.select("#json_sources")
// config references SPINNER RELATED
var chartConfig = {
target : 'chart',
data_url : './nuts0.json',
width: 600,
height: 500,
val: 90
};
// loader settings SPINNER RELATED
var opts = {
lines: 9, // The number of lines to draw
length: 9, // The length of each line
width: 5, // The line thickness
radius: 14, // The radius of the inner circle
color: '#EE3124', // #rgb or #rrggbb or array of colors
speed: 1.9, // Rounds per second
trail: 40, // Afterglow percentage
className: 'spinner', // The CSS class to assign to the spinner
};
// SPINNER RELATED
var target = document.getElementById(chartConfig.target);
// KICK OFF callback function wrapped for loader in 'init' function
function init() {
// trigger loader initial spinner
var spinner = new Spinner(opts).spin(target);
// load json data and trigger callback
d3.json(chartConfig.data_url, function(data) {
// stop spin.js loader
spinner.stop();
// instantiate chart within callback
chart(data);
});
}
//call that init function we define above
init();
//here where all the real stuff happens
//in fact all that init stuff is just legacy
//from the spinner example
function chart(data) {
//start of map making function
var change = function() {
// trigger loader of the spinner
var spinner = new Spinner(opts).spin(target);
// did they change the NUTS?
var source = dropdown.node().options[dropdown.node().selectedIndex].value;
//necessary data processing
var str1 = source;
var str2 = ".json";
var file = str1.concat(str2);
console.log(file);
d3.json(file, function(error, europe) {
d3.csv("povertry_rate.csv", function(error, povrate) {
//change the map to apadpt to the nuts file
if (source == "nuts1") {
var land = topojson.feature(europe, europe.objects.nuts1);
} else if (source == "nuts2") {
var land = topojson.feature(europe, europe.objects.nuts2);
} else if (source == "nuts3") {
var land = topojson.feature(europe, europe.objects.nuts3);
} else if (source == "nuts0") {
var land = topojson.feature(europe, europe.objects.nuts0);
}
data = {};
povrate.forEach(function(d) {
data[d.GEO] = d['2013'];
});
//clear way for the regeneration
d3.selectAll("path").remove();
//recreate those map lines
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// stop spin.js loader
spinner.stop();
console.info(data);
svg
.selectAll("path")
.data(land.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke","#000")
.style("stroke-width",".5px")
.style("fill",function(d){
var value = data[d.id];
if (isNaN(value)){
value = data[d.id.substring(0,2)];
}
if (isNaN(value)){
return "#fff";
}
return scale(value);
})
.on("mouseover", function(d,i) {
var value = data[d.id];
if (isNaN(value)){
value = data[d.id.substring(0,2)];
}
div.transition()
.duration(200)
.style("opacity", 0.9);
div.html("<b>"+d.properties.name+"</b><br/>" + value + "%")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d,i) {
div.transition()
.duration(500)
.style("opacity", 0);
});
svg
.append("path")
.style("fill","none")
.style("stroke","#000")
.attr("d", projection.getCompositionBorders());
});
})
}
dropdown.on("change", change)
change(); //call that change function once
}
</script>
</body>
</html>
Not really a d3 question just a little CSS:
svg {
display: block;
margin: auto;
border: 1px solid gray;
}

inexplicable inability to render city name labels on a d3.js map: out of scope NAME?

I'm following the canonical "Let’s Make a Map" tutorial- but to spice things up I'm melding it with one about Germany- so I'm working with slightly different data.
Things are so far working out- barring this minor hiccup- but now I've come to the section "#Displaying Places" which is where you're supposed to show the names of the cities on the map.
The problem is happening in the following line:
.text(function(d) {
if (d.properties.name!=="Berlin" &&
d.properties.name!=="Bremen"){
//for some reason this is undefined
console.log(d.properties.name);
return d.properties.name;
}
})
The value of that console.log(d.properties.name); is always undefined and I can't figure out why!
I suppose it's because name is out of scope for d- but I don't know how to fix it. Is that right? If so- how to fix it? if not- what is the real problem?
Here is what my code looks like- it's pretty concise:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.subunit{fill:#fff;}
.subunit.Nordrhein-Westfalen{ fill: #aba; }
.subunit.Baden-Württemberg{ fill: #bab; }
.subunit.Hessen{ fill: #bcb; }
.subunit.Niedersachsen{ fill: #cbc; }
.subunit.Thüringen{ fill: #cdc; }
.subunit.Hamburg{ fill: #dcd; }
.subunit.Schleswig-Holstein{ fill: #ded; }
.subunit.Rheinland-Pfalz{ fill: #ede; }
.subunit.Saarland{ fill: #efe; }
.subunit.Sachsen-Anhalt{ fill: #fef; }
.subunit.Brandenburg{ fill: #aaa; }
.subunit.Mecklenburg-Vorpommern{ fill: #bbb; }
.subunit.Bayern { fill: #ccc; }
.subunit.Sachsen { fill: #ddd; }
.subunit.Bremen { fill: #eee; }
.subunit.Berlin { fill: #fff; }
.subunit-boundary {
fill: none;
stroke: #777;
stroke-dasharray: 2,2;
stroke-linejoin: round;
}
.place,
.place-label {
fill: #444;
font-size:14px;
}
text {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 20px;
pointer-events: none;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 1160;
var projection = d3.geo.mercator()
.center([10.5, 51.35])
.scale(3000)
.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);
d3.json("de.json", function(error, de) {
//colouring the different subunits
svg.selectAll(".subunit")
.data(topojson.feature(de, de.objects.subunits).features)
.enter().append("path")
.attr("class", function(d) {
// console.log(d.properties.name);
return "subunit " + d.properties.name;
})
.attr("d", path);
//adding a border to the states
svg.append("path")
.datum(topojson.mesh(de, de.objects.subunits, function(a,b) {
if (a!==b ||
a.properties.name === "Berlin"||
a.properties.name === "Bremen"){
var ret = a;
}
return ret;
}))
.attr("d", path)
.attr("class", "subunit-boundary");
// add small black dots for populated places
svg.append("path")
.datum(topojson.feature(de, de.objects.places))
.attr("d", path)
.attr("class", "place");
//trying to display names of cities
svg.selectAll(".place-label")
.data(topojson.feature(de, de.objects.places).features)
.enter().append("text")
.attr("class", "place-label")
.attr("transform", function(d) {
//small test
//console.log( "translate(" + projection(d.geometry.coordinates) + ")" );
return "translate(" + projection(d.geometry.coordinates) + ")";
})
.attr("dy", ".35em")
.text(function(d) {
if (d.properties.name!=="Berlin" &&
d.properties.name!=="Bremen"){
//for some reason this is undefined
console.log(d.properties.name);
return d.properties.name;
}
})
.attr("x", function(d) {
return d.geometry.coordinates[0] > -1 ? 6 : -6;
})
.style("text-anchor", function(d) {
return d.geometry.coordinates[0] > -1 ? "start" : "end";
});
});
</script>
Here is the data file.
EDIT
expected
actual
Inside your .topojson you have two section:
properties: names of your counties and polygons
places: coordinates of the points
You access the first collection with:
de.objects.subunits
And the second collection through:
de.subunits.places
After file is loaded sepearte into two different variables to use it:
d3.json("de.json", function(error, de) {
var counti = topojson.feature(de, de.objects.subunits)
var places = topojson.feature(de, de.objects.places)
then referenciate the content adding .features
.data(counti.features) // <-- to draw your paths and get the .name: München
or
.data(places.features) // <-- to draw the circles for the cities: "coordinates": [11.573039376427117, 48.131688134368815]
Mike's topojson has:
{
"type": "Feature",
"properties": {
"name": "Ayr"
},
"geometry": {
"type": "Point",
"coordinates": [
-4.617021378468872,
55.44930882146421
]
}
and you has:
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
11.573039376427117,
48.131688134368815
]
}
Mike's point properties looks like this:
and point coordinates
Your point properties:
Solution:
The right way
Open your map on GIS software (ArcGIS-pay, Q-GIS-free) edit and correct paths and points properties and export as TopoJSON again.-
Easy way
Go to: geojson.io load your json and add propertie name to your point (16 points, easy cake) and save as TopoJSON again.-
Now you're there correct countie info deleting one column (you has duplicate info)
#Klaujesi has explained the reason pretty well.
I am just going to solve the issue by a work around.
Since there is no property inside the Feature, so you can get the property from de.objects.subunits, like below.
.text(function(d, i) {
//here i is the index of the place.
//de.objects.subunits.geometries[i].properties will give you the name that you are looking for.
d.properties = de.objects.subunits.geometries[i].properties;
if (d.properties.name!=="Berlin" &&
d.properties.name!=="Bremen"){
//for some reason this is undefined
console.log(d);
return d.properties.name;
}
})
working code here

D3 line graph appearing as area graph

I have drawn a line graph and area under the line is appears to be colored to make it appear like a area graph. The code is shown below
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
body {
font: 12px Arial;
}
path {
stroke-width: 1;
stroke : 1;
}
.axis path,.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<script type="text/javascript" src="d3.min.js"></script>
<script type="text/javascript" src="jquery-1.8.0.js"></script>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
var baseSvg = d3.select("body")
.append("svg")
.attr("height",800)
.attr("width",800)
.append("g")
.attr("transform","translate(50,50)");
$.ajax({
method : 'GET',
url:"URL", //Called my URL here
success:function(data){
var res = data.d.results;
/* res.forEach(function(d){
console.log(new Date(parseInt(d.DATE_SQL.substring(6))));
}) */
buildTrend(res);
}
}) ;
function buildTrend(res) {
var x = d3.time.scale().range([ 50, 700 ]);
var y = d3.scale.linear().range([ 500, 0 ]);
res.forEach(function(d){
d.DATE_SQL = new Date(parseInt(d.DATE_SQL.substring(6)));
});
var line = d3.svg.line().interpolate("basis").x(function(d) {
return x(d.DATE_SQL)
}).y(function(d) {
return y(d.M_COUNT)
});
x.domain(d3.extent(res, function(d) {
return d.DATE_SQL;
}));
y.domain([0,d3.max(res, function(d) {
return d.M_COUNT;
})]);
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(15);
baseSvg.append("g")
.attr("class", "x axis").attr("transform",
"translate(0," + 500 + ")").call(xAxis)
.selectAll("text").attr("transform",function(d){ return "rotate(-90)"})
.attr("dx", "-.8em").attr("dy", ".15em").style("text-anchor", "end");
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(8);
baseSvg.append("g")
.attr("transform","translate(50,0)")// Add the Y Axis
.attr("class", "y axis").call(yAxis);
baseSvg.append("g")
.attr("transform","translate(0,10)")
.append("path")
.attr("d",line(res))
.attr("stroke","blue");
}
</script>
</body>
</html>
However the result looks like this
I have checked all my code to search "Black" to identify possible cause of the color and i dont find any. Maybe it is default color. Cannot figure out the reason for this.
Thanks,
Veera
Try adding this css to your line:
path line {
fill: none;
stroke: #000;
}
I've had this happen to me in the past, if I remember correctly what is happening is that d3 thinks that the first point and the last point on the line are joined and therefore making it an area and filling it by default with the color black.
If you set the fill to none and add a stroke it should fix this.
Hope this helps.

Categories

Resources