【发布时间】:2020-09-01 19:20:54
【问题描述】:
我正在关注这个tutorial 来构建一个可刷的散点图和等值线图。
我还想通过在 choropleth 上添加工具提示来显示州名。
问题可能是如何在 json 文件中获取状态名称。
我尝试了features.properties.name,但得到了错误:未捕获的类型错误:无法读取未定义的属性“名称”。
有人可以帮忙吗?
谢谢!
<!DOCTYPE html>
<meta charset="utf-8">
<style>
div { float: left; }
</style>
<body>
<!-- <svg width="760" height="400"></svg> -->
<div id="choropleth"></div>
<div id="scatterplot"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script>
d3.queue()
.defer(d3.csv, 'https://raw.githubusercontent.com/irrie/myprojects/master/usage0.csv', function (d) {
return {
name: d.State,
penetration: +d.Facebook_Penetration,
young_proportion: +d.YoungProportion
}
})
.defer(d3.json, 'https://raw.githubusercontent.com/python-visualization/folium/master/tests/us-states.json')
.awaitAll(initialize)
var color = d3.scaleThreshold()
.domain([0.3, 0.44, 0.6])
.range(['#fbb4b9', '#f768a1', '#c51b8a', '#7a0177'])
function initialize(error, results) {
if (error) { throw error }
var data = results[0]
var features = results[1].features
var components = [
choropleth(features),
scatterplot(onBrush)
]
function update() {
components.forEach(function (component) { component(data) })
}
function onBrush(x0, x1, y0, y1) {
var clear = x0 === x1 || y0 === y1
data.forEach(function (d) {
d.filtered = clear ? false
: d.young_proportion < x0 || d.young_proportion > x1 || d.penetration < y0 || d.penetration > y1
})
update()
}
update()
}
function scatterplot(onBrush) {
var margin = { top: 10, right: 15, bottom: 40, left: 75 }
var width = 360 - margin.left - margin.right
var height = 300 - margin.top - margin.bottom
var x = d3.scaleLinear()
.range([0, width])
var y = d3.scaleLinear()
.range([height, 0])
var xAxis = d3.axisBottom()
.scale(x)
// .tickFormat(d3.format('.2s'))
var yAxis = d3.axisLeft()
.scale(y)
.tickFormat(d3.format('.0%'))
var brush = d3.brush()
.extent([[0, 0], [width, height]])
.on('start brush', function () {
var selection = d3.event.selection
var x0 = x.invert(selection[0][0])
var x1 = x.invert(selection[1][0])
var y0 = y.invert(selection[1][1])
var y1 = y.invert(selection[0][1])
onBrush(x0, x1, y0, y1)
})
var svg = d3.select('#scatterplot')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
//.attr("transform", "translate(-370,390)")
.append('g')
.attr("transform", "translate(50,10)")
// .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
var bg = svg.append('g')
var gx = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
var gy = svg.append('g')
.attr('class', 'y axis')
gx.append('text')
.attr('x', width)
.attr('y', 35)
.style('text-anchor', 'end')
.style('fill', '#000')
.style('font-weight', 'bold')
.text('Young Proportion')
gy.append('text')
.attr('transform', 'rotate(-90)')
.attr('x', 0)
.attr('y', -40)
.style('text-anchor', 'end')
.style('fill', '#000')
.style('font-weight', 'bold')
.text('Facebook Penetration')
svg.append('g')
.attr('class', 'brush')
.call(brush)
return function update(data) {
x.domain(d3.extent(data, function (d) { return d.young_proportion })).nice()
y.domain(d3.extent(data, function (d) { return d.penetration })).nice()
gx.call(xAxis)
gy.call(yAxis)
var bgRect = bg.selectAll('rect')
.data(d3.pairs(d3.merge([[y.domain()[0]], color.domain(), [y.domain()[1]]])))
bgRect.exit().remove()
bgRect.enter().append('rect')
.attr('x', 0)
.attr('width', width)
.merge(bgRect)
.attr('y', function (d) { return y(d[1]) })
.attr('height', function (d) { return y(d[0]) - y(d[1]) })
.style('fill', function (d) { return color(d[0]) })
var circle = svg.selectAll('circle')
.data(data, function (d) { return d.name })
circle.exit().remove()
circle.enter().append('circle')
.attr('r', 4)
.style('stroke', '#fff')
.merge(circle)
.attr('cx', function (d) { return x(d.young_proportion) })
.attr('cy', function (d) { return y(d.penetration) })
.style('fill', function (d) { return color(d.penetration) })
.style('opacity', function (d) { return d.filtered ? 0.5 : 1 })
.style('stroke-width', function (d) { return d.filtered ? 1 : 2 })
}
}
function choropleth(features) {
var width = 450
var height = 320
var projection = d3.geoAlbersUsa()
.scale([width * 1.25])
.translate([width / 2, height / 2])
var path = d3.geoPath().projection(projection)
var svg = d3.select('#choropleth')
.append('svg')
.attr('width', width)
.attr('height', height)
svg.selectAll('path')
.data(features)
.enter()
.append('path')
.attr('d', path)
.style('stroke', '#fff')
.style('stroke-width', 1)
.on("mouseenter", function(d) {
d3.select(this)
.style("stroke-width", 1.5)
.style("stroke-dasharray", 0)
d3.select("#choropleth")
.transition()
.style("opacity", 1)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY) + "px")
.text(features.properties.name)}) //here
.on("mouseleave", function(d) {
d3.select(this)
.style("stroke-width", .25)
.style("stroke-dasharray", 1)
d3.select("#Text")
.transition()
.style("opacity", 0.9);
})
return function update(data) {
svg.selectAll('path')
.data(data, function (d) { return d.name || d.properties.name })
.style('fill', function (d) { return d.filtered ? '#ddd' : color(d.penetration) })
}
}
</script>
</body>
(另见这个小提琴:http://jsfiddle.net/swo8r7zk/)
【问题讨论】:
标签: javascript d3.js choropleth