Why does a <h2> Header clip the succeeding SVG element? - javascript

I tried a basic visualization with d3.js and dimple from udacity, however, I observe an effect I can't explain:
I run a simple dimple.js visualization which is embedded in an svg element which works fine.
When I preceed the svg element with a header, ... - see line 27
d3.select("body").append("h2").text("World Cup Attendance");
...the chart within the svg element seems to get translated and clipped at the bottom.
Why does adding this line alter the chart?
To my understanding the SVG element is an independent element which has its own relative coordinate system - not affected by preceding html elements...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://dimplejs.org/dist/dimple.v2.1.2.min.js"></script>
<style>
circle.dimple-series-1 {
fill: red;
}
</style>
<script type="text/javascript">
function draw(data) {
/*
D3.js setup code
*/
"use strict";
var margin = 75,
width = 1400 - margin,
height = 600 - margin;
// this line moves the labels of the x axis
d3.select("body").append("h2").text("World Cup Attendance");
var svg = d3.select("body")
.append("svg")
.attr("width", width + margin)
.attr("height", height + margin)
.append('g')
//.attr("transform", "translate(0,-30)")
.attr('class','chart');
var data = [
{ "year":"1980", "attendance":245000 },
{ "year":"1984", "attendance":245000 },
{ "year":"1988", "attendance":304400 }
];
/*
Dimple.js Chart construction code
*/
var myChart = new dimple.chart(svg, data);
var x = myChart.addTimeAxis("x", "year");
myChart.addMeasureAxis("y", "attendance");
x.dateParseFormat = "%Y";
x.tickFormat = "%Y";
// x.timeInterval = 4;
myChart.addSeries(null, dimple.plot.line);
myChart.addSeries(null, dimple.plot.scatter);
myChart.draw();
};
</script>
</head>
<body>
<script type="text/javascript">
/*
Use D3 (not dimple.js) to load the TSV file
and pass the contents of it to the draw function
*/
draw();
// d3.tsv("world_cup.tsv", draw);
</script>
</body>
</html>

I am going to guess it's because of this dimple method, "dimple._parentHeight". It calculates the height of the parent element of the svg and has a workaround for a firefox issue, so you could see a difference based on the container of the svg (body, in this instance) having or not having another element in it.
Your best bet (and what I've done for a similar reason) is to wrap the svg inside a div which will compute the height correctly :
var svg = d3.select("body")
.append("div")
.append("svg")

Related

How do I create axes from categorical and numerical data using d3.js (version 7.0.0)?

Context: I am a beginner in d3.js and have been at this problem for a few days; I have gone through many solutions pertaining to similar problems on here but even though I've replicated the solutions as closely as possible, the issue persists.
I'm trying to create a bar chart with both axes using this simple CSV file:
Food,Yummy
Apples,6
Green Beans,10
Egg Salad Sandwich, 8
Cookies, 2
Liver,1
Burrito,5
I've tried using the following code to create the axes of a bar chart (x-axis: Food, y-axis: Yummy):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Loading data from a CSV file</title>
<script type="text/javascript" src="../d3.js"></script>
</head>
<body>
<script type="text/javascript">
//Defining svg dimensions
width = 250;
height = 600;
margin = 20;
//Creating svg element
var svg = d3.select("body")
.append("svg")
.attr("height", height)
.attr("width", width);
//Loading csv data
rowConverter = function(d) {
return {
Food: d.Food,
Yummy: +d.Yummy
};
}
data = d3.csv("food.csv", rowConverter).then(function(data) {
console.log(data);
});
//Creating scales for x and y axis
var xSxale = d3.scaleBand()
.domain(data.map(function(d) {return d.Food;}))
.range([0, w-margin]);
var maxYummy = d3.max(data, function(d) {return +d.Yummy;});
var yScale = d3.scaleLinear()
.domain([0, maxYummy])
.range([h-margin, 0]);
</script>
</body>
</html>
For the x-axis, I'm getting the following error: "data.map is not a function". I can't find any other way to get the different Food labels onto the x-axis.
For the y-axis, I'm getting the following error: "Uncaught TypeError: values is not iterable
at Object.max$3 [as max] (d3.js:641)
at (index):39" which seems to be pertaining to the d3.max() function.
I'm unclear as to why both errors are existing since I've seen them accepted as solutions on other questions. Any help would be much appreciated by this beginner developer. Thanks so much in advance.

D3 and Leaflet - svg circles not showing

I am trying to draw SVG circles on a map background, but while they are showing up in the elements (using Chrome Dev tools) they are not shown on the page. What am I missing here, why are they hidden?
I have tried to change the fill, opacity of the map and of the circle but I can't figure out why it isn't rendering?
My code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Leaflet and D3 map</title>
<link rel="stylesheet" href="../leaflet.css" />
<script type="text/javascript" src="../leaflet.js"></script>
<script type="text/javascript" src="../d3.js"></script>
<style type="text/css">
#map{
width: 700px;
height: 600px;
}
</style>
</head>
<body>
<div id="map"></div>
<script type="text/javascript">
//
// LOAD THE MAP FROM MAPBOX & LEAFLET
//
var map = L.map("map").setView([50.0755,14.4378], 12);
mapLink = 'Mapbox';
L.tileLayer (
"link to mapbox",{
attribution:"© " + mapLink + " Contributors",
maxZoom:20,
}).addTo(map);
//
// Create the SVG layer on top of the map
//
L.svg().addTo(map);
// Create the standard variables selecting the SVG in the map element
var svg = d3.select("#map").append("svg");
var g = svg.append("g");
//Load the coordinate for the circle
var objects = [ {"circle":{"coordinates":[50.0755,14.4378]}}];
//Loop through to create a LatLng element that can be projected onto Leaflet map
for(var i = 0;i<objects.length;i++){
objects[i].LatLng = new L.LatLng(objects[i].circle.coordinates[0], objects[i].circle.coordinates[1])
};
//Create the circle object and store it in features
var feature = g.selectAll("circle")
.data(objects)
.enter()
.append("circle")
.style("fill", "red")
.attr("r", 20);
//Make the circle dynamic, by calling the update function whenever view is view is reset
map.on("viewreset", update)
//Call the update also on first load of the web page
update();
//Updates the position of the circle every time the map is updated
function update(){
feature.attr("transform",
function(d){
return "translate("+
map.latLngToLayerPoint(d.LatLng).x+","+
map.latLngToLayerPoint(d.LatLng).y+")";
})
};
</script>
</body>
</html>
As you note, your circle is appended:
But, it is invisible not because of opacity or color, but because you don't set the dimensions of the svg. With default dimensions of the svg, your circle is beyond its border and consequently hidden (it resides in the middle of the map, at [350,300], while the default size of an svg is 300x150 likely browser dependent). Try:
var svg = d3.select("#map")
.append("svg")
.attr("width",700)
.attr("height",600)
As your map is 700 pixels across and 600 pixels high.

Get pixel width of d3.js SVG element after it's created with width as percentage

I want to place a svg bar chart into a certain div and fail to get the calculated svg width if I set the attribute "width" as a percentage. I would like to stick to giving the size as a percentage to be able to calculate bar widths etc. from the svg size.
The div for the chart:
<div id="chart"></div>
The code for appending the SVG, place in a function before that. The function is called to put data in a bootrap modal:
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var chartSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","prodchartsvg")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
To get the calculated size in pixels, I already tried this:
$("#prodchartsvg").width(); // not working, returns 100
$("#chart").width(); // not working, returns null
prodchartsvg.node().getBBox(); // prodchartsvg.node is not a function
d3.select("#prodchartsvg").style("width"); // returns 100%
chartSVG.style("width"); // returns auto
d3.select("#prodchartsvg").node().getBoundingClientRect(); // returns 0 for all parameters
d3.select("#chart").node().getBoundingClientRect(); // returns 0 for all parameters
I realise some of the above functions I tried are unsuitable in this context (DOM, object, ...).
Can you give me an idea how to get the svg width in pixels instead of a percentage?
Thank you for the help!
As #nrabinowitz commented, your problem is most probably caused by the fact that your SVG is not visible in the screen at the moment when you attempt to retrieve its width.
Test case
In the following snippet, we compare the value returned by $("#prodchartsvg").width() for two svgs, one hidden and one displayed.
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var hiddenSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","hiddenSVG")
.style('display', 'none') // not shown on screen
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').html('hiddenSVG width is ' + $("#hiddenSVG").width())
var shownSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","shownSVG")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').append('<br>shownSVG width is ' + $("#shownSVG").width())
svg {background-color: #eee}
<div id='chart'></div>
<div id='result'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
How to fix
Try one of these solutions:
make sure the chart is displayed on screen whenever you compute the width
make sure the DOM is loaded whenever you compute the width. Either move you script at the end of the html body, or wrap your code inside a jQuery DOM ready function...

Path not showing in d3.js topoJson graph

I am trying to draw a map using topoJson so I followed this example
http://bl.ocks.org/mbostock/4122298
but I am not getting anything drawn.
here's what i wrote
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #fff;
stroke-width: .5px;
}
path:hover {
fill: red;
}
</style>
</head>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 500;
var path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("tunisia.json", function(error, topology) {
console.log(topology);
svg.selectAll("path")
.data(topojson.feature(topology, topology.objects.governorates).features)
.enter().append("path")
.attr("d", path);
});
</script>
</body>
</html>
After some debugging it turns out that the path in my case is added as follows:
<svg width="960" height="500">
<path></path>
<path></path>
</svg>
whereas it should normally be like this:
<svg width="960" height="500">
<path d="M183.85631949544694,17.16574961388676L184.64695256075555,18.261986556132797L184.24437929962187,21.436416964644536L184.9109502450185,22.72190753660925L183.42733139583214,23.600229178621248L181.43637647772152,23.38526266060535L162.4858998398068,18.04698631290296L162.95134674943927,16.322885588815097L161.24381018256219,15.20848145955324L160.04585728433227,11.701769628478132L161.0879861841512,10.793553936506555L172.9773901748378,14.256236175137701Z"></path>
</svg>
here is the data I am using:
https://raw.githubusercontent.com/mtimet/tnacmaps/master/topojson/tunisia.json
could you please check what I am doing wrong
There is no problem with your json file.
The issue you are having is that you are not defining a projection for your d3.geo.path() which means it falls back to the default. According to the documentation linked above:
#d3.geo.path()
Creates a new geographic path generator with the default settings: the albersUsa projection and a point radius of 4.5 pixels.
Your geo data is for a map of Tunisia, so an albersUsa projection won't contain any of the coordinates in your dataset. That is why the path data is empty in your output.
To fix this, you need to define a projection. You can do this when you load your data, and you can use d3.geo.bounds(), passing in your featureCollection to find the geographic boundaries of your data.
var featureCollection = topojson.feature(topology, topology.objects.governorates);
var bounds = d3.geo.bounds(featureCollection);
Then from these boundaries, you can calculate the center of your featureCollection:
var centerX = d3.sum(bounds, function(d) {return d[0];}) / 2,
centerY = d3.sum(bounds, function(d) {return d[1];}) / 2;
Then you can use this to center your projection. For example, if you're using a mercator projection, you could do the following:
var projection = d3.geo.mercator()
.scale(3000)
.center([centerX, centerY]);
The choice of a scale of 3000 is arbitrary, it just seemed to work well in this case, tweak it to whatever works for you.
Finally, you need to set the .projection() of your path to the projection you made, before actually creating the svg paths.
path.projection(projection);
svg.selectAll("path")
.data(featureCollection.features)
.enter().append("path")
.attr("d", path);
HERE is a working example using your data.

Stop animation in VivaGraph.JS

How I can stop the animation for the nodes in VivaGraph.JS
everything is great but nodes still moving and some nodes come out in the frame.
I can't use this example :
https://github.com/anvaka/VivaGraphJS/blob/master/demos/other/constantLayout.html
because is not good for me the determinate for every node a position.
My code :
<!DOCTYPE html>
<html>
<head>
<title>02. Custom node appearance. Vivagraph SVG tutorial.</title>
<script type="text/javascript" src="../../dist/vivagraph.js"></script>
<script type="text/javascript">
function main () {
// Create a graph:
var graph = Viva.Graph.graph();
for(i=0;i<20;i++)
{
graph.addNode(i, '91bad8ceeec43ae303790f8fe238164b');
}
var graphics = Viva.Graph.View.svgGraphics();
graphics.node(function(node) {
var url = 'https://secure.gravatar.com/avatar/' + node.data;
return Viva.Graph.svg('image')
.attr('width', 24)
.attr('height', 24)
.link(url);
});
graphics.placeNode(function(nodeUI, pos) {
nodeUI.attr('x', pos.x - 12).attr('y', pos.y - 12);
});
// Render the graph with our customized graphics object:
var renderer = Viva.Graph.View.renderer(graph, {
graphics : graphics
});
renderer.run();
}
</script>
<style type="text/css" media="screen">
html, body, svg { width: 100%; height: 100%;}
</style>
</head>
<body onload='main()'>
</body>
</html>
I wish I explain good my problem, someone can help me please.
You can use the following methods of the renderer object to accomplish this.
To start the rendering - renderer.run();
To pause the rendering - renderer.pause();
To resume the rendering - renderer.resume();
To reset the scale of the rendering to fit all elements - renderer.reset();
To remove the rendering - renderer.dispose();
Hope this is helpful...

Categories

Resources