Reputation: 115
new to D3 so I appreciate your patience! I've made a map where circles appended to city coordinates are sized upon a value in a .csv. I've also added in a click-to-zoom function from one of Mike Bostock's examples.
However, the circles don't resize when you zoom in. I'm not sure how to go about having them re-size or redraw upon zoom in and zoom out?
Any tips, examples, or suggestions are welcome! Here's my code:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta charset='utf-8'>
<meta name="viewport" content='width=device-width, initial-scale=1.0'>
<title></title>
<style>
.tooltip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
</style>
<script>
function draw(geo_data) {
var margin = 20,
width = 1500 - margin,
height = 500 - margin;
var centered;
var svg = d3.select('#map')
.append('svg')
.attr('width', width + margin)
.attr('height', height + margin)
.append('g')
.attr('class', 'map');
var formatComma = d3.format(",")
var projection = d3.geoAlbersUsa();
var path = d3.geoPath().projection(projection);
var map = svg.selectAll('path')
.data(geo_data.features)
.enter()
.append('path')
.attr('d', path)
.attr('fill', 'rgba(253, 227, 167, 0.8)')
.attr('stroke', 'black')
.attr('stroke-width', 0.4)
.on('mouseover', function(d) {
return d3.select(this).attr('fill', 'rgba(149, 165, 166, 0.8)')
})
.on('mouseout', function() {
return d3.select(this).attr('fill', 'rgba(253, 227, 167, 0.8)')
})
// Why is this not working???
.on('click', function() {
return d3.select(this).attr('fill', 'rgba(149, 165, 166, 0.8)')
})
// Calls click-to-zoom function defined below.
.on('click', clicked);
var tooltip = svg.append('g')
.attr('class', tooltip);
tooltip.append('text')
.attr('x', 15)
.attr('class', 'tooltip')
.attr('dy', '1.2em')
.style('font-size', '1.5em')
.attr('font-weight', 'bold');
// Click-to-zoom function taken from Mike Bostock's code: https://bl.ocks.org/mbostock/2206590
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;
}
map.selectAll('path')
.classed('active', centered && function(d) {
return d === centered;
});
map.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
.style('stroke-width', 1 / k + 'px');
}
d3.csv('top100cities.csv', function(error, data) {
// Converts strings in csv to integers so they can be used.
data.forEach(function(d) {
return d.guns = +d.guns;
})
// This returns the max number of guns.
var guns = data.map(function(d) {
return d.guns;
});
var guns_extent = d3.extent(data, function(d) {
return d.guns;
});
var radius = d3.scaleSqrt()
.domain(guns_extent)
.range([0, 50]);
svg.append('g')
.attr('class', 'bubble')
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d) {
return projection([d.lon, d.lat])[0];
})
.attr('cy', function(d) {
return projection([d.lon, d.lat])[1];
})
.attr('r', function(d) {
return radius(d.guns);
})
.attr('fill', 'rgba(103, 65, 114, 0.5)')
.attr('stroke', 'black')
.on('mouseover', function(d) {
return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.8)');
tooltip.select('text').style('opacity', 1);
})
.on('mousemove', function(d) {
var xPos = d3.mouse(this)[0] - 15;
var yPos = d3.mouse(this)[1] - 55;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('text').style('opacity', 1);
tooltip.select('text').text(d.city + ', ' + d.state + ': ' + formatComma(d.guns));
})
.on('mouseout', function(d) {
tooltip.select('text').style('opacity', 0);
return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.5)');
});
});
};
</script>
</head>
<body>
<div id='map'></div>
<script>
d3.json('us_states.json', draw);
</script>
</body>
Upvotes: 1
Views: 921
Reputation: 61666
You can do exactly the same as you did for the map:
Within the clicked
function, just after the transform
on path
items, you can insert the following transformation which applies on circles
:
svg.selectAll('circle')
.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
.style('stroke-width', 1 / k + 'px');
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<meta charset='utf-8'>
<meta name="viewport" content='width=device-width, initial-scale=1.0'>
<title></title>
<style>
.tooltip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 2px;
}
</style>
<script>
function draw(geo_data) {
var margin = 20,
width = 1500 - margin,
height = 500 - margin;
var centered;
var svg = d3.select('#map')
.append('svg')
.attr('width', width + margin)
.attr('height', height + margin)
.append('g')
.attr('class', 'map');
var formatComma = d3.format(",")
var projection = d3.geoAlbersUsa();
var path = d3.geoPath().projection(projection);
var map = svg.selectAll('path')
.data(geo_data.features)
.enter()
.append('path')
.attr('d', path)
.attr('fill', 'rgba(253, 227, 167, 0.8)')
.attr('stroke', 'black')
.attr('stroke-width', 0.4)
.on('mouseover', function(d) {
return d3.select(this).attr('fill', 'rgba(149, 165, 166, 0.8)')
})
.on('mouseout', function() {
return d3.select(this).attr('fill', 'rgba(253, 227, 167, 0.8)')
})
// Why is this not working???
.on('click', function() {
return d3.select(this).attr('fill', 'rgba(149, 165, 166, 0.8)')
})
// Calls click-to-zoom function defined below.
.on('click', clicked);
var tooltip = svg.append('g')
.attr('class', tooltip);
tooltip.append('text')
.attr('x', 15)
.attr('class', 'tooltip')
.attr('dy', '1.2em')
.style('font-size', '1.5em')
.attr('font-weight', 'bold');
// Click-to-zoom function taken from Mike Bostock's code: https://bl.ocks.org/mbostock/2206590
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;
}
map.selectAll('path')
.classed('active', centered && function(d) {
return d === centered;
});
map.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
.style('stroke-width', 1 / k + 'px');
svg.selectAll('circle')
.transition()
.duration(750)
.attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')scale(' + k + ')translate(' + -x + ',' + -y + ')')
.style('stroke-width', 1 / k + 'px');
}
d3.csv('https://raw.githubusercontent.com/dieterholger/US-Gun-Manufacturing-Interactive/master/top100cities.csv', function(error, data) {
// Converts strings in csv to integers so they can be used.
data.forEach(function(d) {
return d.guns = +d.guns;
})
// This returns the max number of guns.
var guns = data.map(function(d) {
return d.guns;
});
var guns_extent = d3.extent(data, function(d) {
return d.guns;
});
var radius = d3.scaleSqrt()
.domain(guns_extent)
.range([0, 50]);
svg.append('g')
.attr('class', 'bubble')
.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx', function(d) {
return projection([d.lon, d.lat])[0];
})
.attr('cy', function(d) {
return projection([d.lon, d.lat])[1];
})
.attr('r', function(d) {
return radius(d.guns);
})
.attr('fill', 'rgba(103, 65, 114, 0.5)')
.attr('stroke', 'black')
.on('mouseover', function(d) {
return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.8)');
tooltip.select('text').style('opacity', 1);
})
.on('mousemove', function(d) {
var xPos = d3.mouse(this)[0] - 15;
var yPos = d3.mouse(this)[1] - 55;
tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
tooltip.select('text').style('opacity', 1);
tooltip.select('text').text(d.city + ', ' + d.state + ': ' + formatComma(d.guns));
})
.on('mouseout', function(d) {
tooltip.select('text').style('opacity', 0);
return d3.select(this).attr('fill', 'rgba(103, 65, 114, 0.5)');
});
});
};
</script>
</head>
<body>
<div id='map'></div>
<script>
d3.json('https://raw.githubusercontent.com/dieterholger/US-Gun-Manufacturing-Interactive/master/us_states.json', draw);
</script>
</body>
Upvotes: 3