I am trying to load the NY map using Taxi data JSON file, but i am not able to view the correct output. It outputs a very weird map. I tried to search for some more online reference, but could not find.
Currently the code is trying to load the taxi data using taxi_zone.json.
here is my code:
Image with current output
<script src="../lib/d3.v3.min.js"></script>
<script src="../lib/d3-queue.v3.min.js"></script>
<script src="../lib/topojson.v1.min.js"></script>
<script src="../lib/d3.tip.v0.6.3.js"></script>
<script>
var margin = {top:100, right:100, bottom: 10, left: 100}
var width = 1200-margin.left-margin.right,
height = 800-margin.bottom-margin.top;;
//var path = d3.geo.path();
var nameById = d3.map();
var svg = d3.select("#map").append("svg")
.attr("width", width+margin.left+margin.right)
.attr("height", height+margin.bottom+margin.top);
svg.append("text")
.attr("x", (width / 2))
.attr("y", (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "30px")
.style("font-weight","Bold");
tip = d3.tip()
.attr('class', 'd3-tip')
.style('opacity', 0.5)
.offset([-10, 0])
.direction('n')
.html(function(d) {
return "City: "+ nameById.get(d.properties.zone)
});
svg.call(tip);
d3.queue()
.defer(d3.json, "taxi_zones.json")
.await(ready);
function ready(error, taxi_zones) {
if (error) throw error;
console.log(taxi_zones);
svg.append("g")
.attr("class", "counties")
.attr("transform","translate(20,50)")
.attr("stroke","#f2f2f2")
.selectAll("path")
.data(topojson.feature(taxi_zones, taxi_zones.objects.taxi_zones).features)
.enter().append("path")
.attr("d", d3.geo.path())
.style("fill", "grey")
.on('mouseover',tip.show)
.on('mouseout', tip.hide);
svg.append("path")
.datum(topojson.mesh(taxi_zones, taxi_zones.objects.taxi_zones, function(a, b) { {console.log(a)} return a.LocationID !== b.LocationID; }))
.attr("class", "states")
.attr("d", d3.geo.path().projection(d3.geo.mercator().scale(50000)));
}
</script>
Can you please help me with what i am doing wrong here.
I have uploaded the JSON file here:
https://jsonblob.com/97dc4332-ecf4-11e8-bcc5-511f7ee4aaef
Related
I have this code, which creates a horizontal bar chart, and it fits nicely to any screen width , but only when the document loads or reload. When I rotate my mobile to landscape mode it does not re-render again according to the new dimensions of the screen, despite I tried to use a addEventLisenet for resize event.
I'll leave a running snippet of my code so that you can see what I am doing wrong.
Thanks a lot for the help.
var data = [72,70,65,58,51,50,21,15,13]
var categoryData = ["International assignment allowance and benefits.","Tax and social security cost.","Relocation costs.","Prepared at start of assigment.","Assignee-specific data.","Vendor fees.","Incentive compensation (projected).","Employee benefit plan contributions and deduction.","They are rough calculations."/*"They are detailed and precise"/*,"General data (not assignee-specific)","Updated annually","Updated for change in assignement policy","Updated for approved policy exceptions"*/]
var width = $(window).width(),
barHeight = 20,
margin = {left:15,top:15,right:15,bottom:15}
var widthScale = d3.scaleLinear()
.domain(d3.extent(data,function(d){return d }))
.range([50, width - 40]);
var svg = d3.select("body")
.append("svg")
.attr("width", width - margin.left - margin.right)
.attr("height"," 100vh")
.attr("id","SVGCanvas")
var mainTitle = svg.append("text")
.attr("class","mainTitle")
.html("Which of the following statements describe your")
.attr("transform","translate(5,60)")
.attr("font-weight","bold")
var mainTitle2 = svg.append("text")
var mainTitle2 = svg.append("text")
.attr("class","mainTitle2")
.html("approach to cost estimates? (Select all that apply.)")
.attr("transform","translate(5,78)")
.attr("font-weight","bold")
.style("float","left")
var mainBarG = svg.append("g")
.attr("class","mainBarG")
.attr("transform","translate(5,142)")
var g = mainBarG.selectAll("g")
.data(data)
.enter()
.append("g")
.attr("class","bGroup")
.attr("transform", function (d, i) {
return "translate(0," + ((i * barHeight*3) ) + ")";
});
g.append("rect")
.attr("width",0)
.transition()
.duration(800)
.delay(function(d,i){return i*25})
.attr("width", function (d) {
return widthScale(d);
})
.attr("height", barHeight*1.3 )
.attr("class","bars")
.attr("fill","#00338D")
.attr("id", function (d,i) {
return "barra" + i;
})
g.append("text")
.attr("x", function (d) { return widthScale(d) })
.attr("y", barHeight / 2)
.attr("dy", ".6em")
.attr("dx", "-2.5em")
.text(function (d) { return d + '%' })
.attr("font-size", 13)
.attr("font-weight", "bold")
.attr( "rx","20")
.attr( "ry","20")
.attr("class","barsText")
.attr("fill","#FFF")
g.append("text")
.data(categoryData)
.attr("x", 0)
.attr("y", (barHeight / 2) - barHeight *.8)
.attr("font-size", 13)
.attr("class","barsText")
.text(function (d) { return d })
setTimeout(function(){
$(".barsText").animate({opacity: 1});
},900);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
i have code that works outside of Play Framework but gives me errors when i implement the code in Play. When i bind d3.js it gives me the following error:
"Uncaught TypeError: Cannot read property 'length' of undefined
at Object.ms [as extent] (d3.v4.min.js:2)
at Object.<anonymous> ((index):103)
at p.call (d3.v4.min.js:2)
at XMLHttpRequest.e (d3.v4.min.js:2)"
Why does this happen? I definitely integrated the sources correctly. All other sources work perfectly.
EDIT: Play Framework version 2.6.6 and sbt version 0.13.5
Requested code:
<script language="javascript" type="text/javascript">
window.onload = function() {
var svg = d3.select("svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var parseTime = d3.timeParse("%d-%b-%y");
var x = d3.scaleTime()
.rangeRound([0, width]);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var line = d3.line()
.x(function (d) {
return x(d.date);
})
.y(function (d) {
return y(d.close);
});
d3.tsv("/assets/images/data.tsv", function (d) {
d.date = parseTime(d.date);
d.close = +d.close;
return d;
}, function (error, data) {
x.domain(d3.extent(data, function (d) {
return d.date;
}));
y.domain(d3.extent(data, function (d) {
return d.close;
}));
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(0)")
.attr("x", 6)
.attr("dx", "1em")
.attr("text-anchor", "begin ")
.text("time")
.select(".domain")
.remove();
g.append("g")
.call(d3.axisLeft(y))
.append("text")
.attr("fill", "#000")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Price ($)");
g.append("path")
.datum(data)
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-linejoin", "round")
.attr("stroke-linecap", "round")
.attr("stroke-width", 1.5)
.attr("d", line);
});
}
</script>
This is the d3 script that is on the bottom of the html file outside of the body.
In the body itself i have amongst other things this SVG object that is used:
<svg id="svg" width="960" height="500"></svg>
As i said this works perfectly outside of play. But if i start play it just throws that error.
I have the following d3.js script to create bar chart which works fine.
I added functionality to show tool tip (not sure whether i added it at right place or not) which works fine but it has created an issue with existing mouseout event.
Issue:
The issue is that the following code is not working anymore. When i mouse over it does not turn into grey.
.on('mouseout', function (d) {
d3.select(this)
.attr('fill', 'blue');
However, if i comment the following lines than above mouseout event works perfect.
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
Complete Script
var jsonData = #Html.Raw(Json.Encode(Model));
data = jsonData;
InitChart();
function InitChart() {
var barData = data;
var vis = d3.select('#SummaryChart'),
WIDTH = 500,
HEIGHT = 375,
MARGINS = {
top: 20,
right: 20,
bottom: 20,
left: 150
},
xRange = d3.scale.ordinal().rangeRoundBands([MARGINS.left, WIDTH - MARGINS.right], 0.1).domain(barData.map(function (d) {
return d.Date;
})),
yRange = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]).domain([0,
d3.max(barData, function (d) {
return d.Duration;
})
]),
xAxis = d3.svg.axis()
.scale(xRange)
.tickSize(0)
.tickSubdivide(true),
yAxis = d3.svg.axis()
.scale(yRange)
.tickSize(0)
.orient("left")
.tickSubdivide(true);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Duration:</strong> <span style='color:red'>" + d.Duration + "</span>";
})
vis.call(tip);
vis.append('svg:g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + (HEIGHT - MARGINS.bottom) + ')')
.call(xAxis);
vis.append('svg:g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + (MARGINS.left) + ',0)')
.call(yAxis);
vis.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("font-size", "20px")
.attr("x", WIDTH)
.attr("y", HEIGHT + 20)
.text("Time");
vis.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("font-size", "20px")
.attr("y", 100)
.attr("x",-100)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Hours:");
vis.selectAll('rect')
.data(barData)
.enter()
.append('rect')
.attr('x', function (d) {
return xRange(d.Date);
})
.attr('y', function (d) {
return yRange(d.Duration);
})
.attr('width', xRange.rangeBand())
.attr('height', function (d) {
return ((HEIGHT - MARGINS.bottom) - yRange(d.Duration));
})
.attr('fill', 'blue')
.on('mouseover', function (d) {
d3.select(this)
.attr('fill', 'grey');
})
.on('mouseout', function (d) {
d3.select(this)
.attr('fill', 'blue');
})
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
}
</script>
Can someone point out what is wrong and how i cam make it working with both tooltip and on mouse turning into grey working both together?
Unlike jQuery, D3 allows only a single callback per action. Therefore if you attach two .on('mouseout') callbacks, only the last one will execute. See:
d3.select('div')
.on('mouseout', function() {console.log('A')})
.on('mouseout', function() {console.log('B')})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div style='width:250px;height:250px;background:red'></div>
You have two ways around this. One, as suggested in comments, would be to call both attr and tooltip in the callback, as this:
.on('mouseout', function (d) {
d3.select(this).attr('fill', 'blue');
tip.hide();
}
Second would be to use the dot . notation, as described in the API, second paragraph
If an event listener was already registered for the same type on the selected element, the existing listener is removed before the new listener is added. To register multiple listeners for the same event type, the type may be followed by an optional namespace, such as "click.foo" and "click.bar".
So in your case
.on('mouseout.attr', function (d) {
d3.select(this)
.attr('fill', 'blue');
})
.on('mouseout.tip', tip.hide)
Working example:
d3.select('div')
.on('mouseout.logA', function() {console.log('A')})
.on('mouseout.logB', function() {console.log('B')})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div style='width:250px;height:250px;background:red'></div>
I'm trying to draw pie charts in Meteor, but I'm very new to both d3 and Meteor and am not really understanding what is going on.
The following d3 code to draw pie charts from a csv file works for me outside of Meteor:
<!DOCTYPE html>
<meta charset="utf-8">
<link href='http://fonts.googleapis.com/css?family=Montserrat:400,700' rel='stylesheet' type='text/css'>
<style>
body {
font: 30px "Montserrat";
text-transform:uppercase;
}
svg {
padding: 10px 0 0 10px;
}
.arc {
stroke: #fff;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var radius = 150,
padding = 10;
var color = d3.scale.ordinal()
.range(["#f65c55","#c8e7ec"]);
var arc = d3.svg.arc()
.outerRadius(radius)
.innerRadius(radius - 40);
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return d.population; });
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Criteria"; }));
data.forEach(function(d) {
d.ages = color.domain().map(function(name) {
return {name: name, population: +d[name]};
});
});
var legend = d3.select("body").append("svg")
.attr("class", "legend")
.attr("width", radius * 2)
.attr("height", radius * 2)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * 50 + ")"; });
legend.append("rect")
.attr("width", 40)
.attr("height", 40)
.style("fill", color);
legend.append("text")
.attr("x", 50)
.attr("y", 20)
.attr("dy", ".35em")
.attr("font-size","20px")
.text(function(d) { return d; });
var svg = d3.select("body").selectAll(".pie")
.data(data)
.enter().append("svg")
.attr("class", "pie")
.attr("width", radius * 2)
.attr("height", radius * 2)
.append("g")
.attr("transform", "translate(" + radius + "," + radius + ")");
svg.selectAll(".arc")
.data(function(d) { return pie(d.ages); })
.enter().append("path")
.attr("class", "arc")
.attr("d", arc)
.style("fill", function(d) { return color(d.data.name); });
svg.append("text")
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.Criteria; });
});
</script>
I also have a Meteor template as follows that I want to draw these charts in:
<div class="tab-pane active" id="playback">
{{> playback}}
</div>
However, when I try and follow web tutorials to integrate the two, the graphs don't get drawn. Can anyone help me understand why? Thanks in advance!
EDIT: forgot to mention, data.csv looks like this:
Criteria,Disapproval, Approval
Too Fast,1,2
Too Slow,5,6
Clarity,2,3
Legibility,202070,343207
The first line is for the legend, and the rest are for 4 separate graphs.
You have to make sure that the template is rendered before you access the DOM elements by code. So put your D3 code inside a template rendered method, like this:
Template.playback.rendered = function() {
// your D3 code
}
or on the body tag e.g.:
UI.body.rendered = function() {
// your D3 code
}
Template.chart.rendered = function(){
Deps.autorun(function () {
//d3 codeing here!!
}
}
It's working for me. If you're coding without Deps.autorun() it's will not render.
Oh!! one morething at html page in you case maybe .
However for my case I using nvd3.org http://nvd3.org/livecode/index.html#codemirrorNav. And this I hope you will clearify.
Been away from d3.js for a few months... and I've inherited a simple US map with features that someone else started.
The features are represented by simple dots of varying sizes.
I want to add emanating concentric circles to each dot, similar to the classic Onion example by Mike Bostock: http://bl.ocks.org/mbostock/4503672 (maybe not so ominous looking though)
I've got a block set up here: http://bl.ocks.org/mbostock/4503672
(Not sure why the states aren't rendering correctly in the block, but it probably doesn't matter for this.)
In Mike's example there is only one dot, so I'm have trouble understanding how to translate what he did to what I've got (many dots).
Here's my script:
/**
* Page initialization
*/
$(function() {
renderMap('#map-container');
});
function renderMap(container) {
var width = 960,
height = 500,
active;
var projection = d3.geo.albersUsa()
.scale(960)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var radius = d3.scale.sqrt()
.domain([0, 1e7])
.range([0, 10]);
var path2 = d3.geo.path()
.projection(projection);
// Remove svg, if already exist
d3.select(container).select('svg').remove();
var svg = d3.select(container).append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("width", width)
.attr("height", height);
//.on("click", reset);
var g = svg.append("g");
queue()
.defer(d3.json, "/mbostock/raw/4090846/us.json")
.defer(d3.json, "dots.json")
.await(function (error, us, centroid) {
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.attr("class", "state");
//.on('click', click);
g.append('path')
.attr("id", "state-borders")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("d", path)
.attr("class", "mesh");
var dots = g.append("g")
.attr("id", "dots")
.selectAll("path")
.data(centroid.data)
.enter().append("path")
.attr("class", "dot")
.attr("d", path2.pointRadius(function(d) { return radius(d.properties.pool); }));
}
);
}
and the key part of Mike's example for making the rings is:
setInterval(function() {
svg.append("circle")
.attr("class", "ring")
.attr("transform", "translate(" + projection([100, -8]) + ")")
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750);
how do I get the rings positioned on the dots?
Review the differences between the two methods to learn a little bit more about how functional/declarative programming abstracts away the pain of iterative programming.
Approach with D3 idioms:
fiddle: http://jsfiddle.net/blakedietz/E66eT/1/
update: D3 Way
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
svg
.selectAll("ring")
.data(data)
.enter()
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle)
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>
So I didn't create a fully d3 idiomatic approach for this solution, but it will work. If you can get this to work implicitly within a svg.selectAll("circle"/"unique selection"...), etc, that would be even more awesome. I'll work on that in the mean time. Until then there's this more explicitly iterative approach.
With Mike's example you're only appending a single element to the D.O.M. in the setInterval call. In order to expedite the binding process, I've created a projection method which operates on a set of coordinates: the translateCircle will operate on a datum within a collection of coordinates allowing access to the internal attributes of each collection element.
Within each setInterval call the forEach method iterates over the collection of coordinates and then calls the same internals within the setInterval method that was called by Mike originally.
Not So D3
<style>
body {
background: #192887;
}
.graticule {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
.land {
fill: #007421;
}
.dot {
fill: #c7141a;
}
.ring {
fill: none;
stroke: #c7141a;
}
</style>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
.center([113, -3])
.scale(1275)
.translate([width / 2, height / 2])
.clipExtent([[0, 0], [width, height]])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule()
.step([5, 5]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
var data = [{x:-8,y:100},{x:-10,y:110},{x: -12,y:120}];
svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","dot")
.attr("transform",translateCircle)
.attr("r",8);
function translateCircle(datum, index)
{
return "translate(" + projection([datum.y, datum.x]) + ")";
};
setInterval(function(){
data.forEach(function(datum)
{
svg
.append("circle")
.attr("class", "ring")
.attr("transform", translateCircle(datum))
.attr("r", 6)
.style("stroke-width", 3)
.style("stroke", "red")
.transition()
.ease("linear")
.duration(6000)
.style("stroke-opacity", 1e-6)
.style("stroke-width", 1)
.style("stroke", "brown")
.attr("r", 160)
.remove();
})
}, 750)
d3.select(self.frameElement).style("height", height + "px");
</script>
</body>
</html>