【问题标题】:d3.js brushable choroplethd3.js 可刷的 choropleth
【发布时间】: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


    【解决方案1】:

    您可以在该范围内使用d.named 还具有每个状态的 penetrationyoung_proportion 属性。这些是在您加载 CSV 数据的文件的开头定义的。

    【讨论】:

    • 谢谢!是的,我试过了,但是整个等值线图消失了,只显示了工具提示文本。 jsfiddle.net/0krjdgam/2
    • 这是正确的,因为您设置了#choropleth 的位置,而不是您的工具提示。
    • 对不起,我不明白。我是否需要在 html 中添加 &lt;div id="tooltip"&gt;&lt;/div&gt; 并将位置更改为 #tooltip ?现在似乎文本不跟随鼠标了。
    • 是的,这是一种方法。您需要处理“mousemove”事件以使工具提示跟随鼠标。可能类似于这个例子bl.ocks.org/biovisualize/1016860
    猜你喜欢
    • 2016-12-23
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-16
    • 1970-01-01
    相关资源
    最近更新 更多