根据用户输入过滤 .csv 数据并刷新 d3.js 地图

Filter .csv data based on user input and refresh d3.js map

我是一个 d3.js 新手,已经能够为一个项目拼凑出一张地图,但现在我被卡住了。我想根据用户 input/selection 在五个维度的任意组合上刷新地图。根据用户输入,我希望我的代码仅筛选满足所选条件的数据行,汇总该地理位置的人数并刷新地图。由于各种项目限制,我必须使用 .csv 或 .json 作为我的数据源。我知道我可以切换数据源并根据用户输入刷新地图,但由于可能的选择组合数量,该解决方案对我的项目不可行。我想我很接近,但我不确定如何越过终点线。我已将用户选择存储在变量中,创建了我的数据的 d3.nest,并且能够在指定的 d3.nest 键上创建 d3.sums。

我想我已经找到 post 可以回答我的问题,但我太菜鸟了,无法理解如何为我的项目综合相关信息。我需要做的是根据用户输入动态创建 d3.nest 和 d3.sum,但我不知道如何 link 我的用户选择变量到我创建巢与总和。我想在用户每次进行选择时刷新地图和数据,但如果更容易的话,我愿意在屏幕上放置一个按钮来刷新数据。这是我的地图的截图 map sample.

以下是我的代码的相关部分:

        // ######################################################################
    // BUILD THE MAP, PLOT THE DATA AND SIZE THE BUBBLES
    // ######################################################################

    var projection = d3.geo.mercator()
        .center([0, 0])
        .scale(225)
        .translate([width / 2, height / 2]);

    var path = d3.geo.path()
        .projection(projection);

    // LOAD THE GEOJSON DATA FOR THE MAP
    // ---------------------------------
    d3.json("world-50m.json", function ready(error, world) {

        g.selectAll('path')
            .data(topojson.feature(world, world.objects.countries).features)
            .enter().append('path')
            .attr("id", function(d) {
                return d.properties.abbreviation;
            })
            .attr("d", path)
            .attr("fill", "#ffcc99")
            .attr("stroke", "#ffffff")
            .on("click", clicked);

        // LOAD THE CSV DATA TO POPULATE THE LOCATIONS
        // AND BUBBLE SIZES ON THE MAP
        // -------------------------------------------
        d3.csv("sample_data.csv", function(data) {

            // INCLUDE ONLY THOSE ROWS OF DATA WITH
            // LATITUDE AND LONGITUDE VALUES AND CONVERT
            // CSV DATA INTO NUMERIC VALUES
            // -----------------------------------------
            data = data.filter(function(d, i) {

                if (d.LATITUDE && d.LONGITUDE) {

                    // CONVERT LATITUDE AND LONGITUDE
                    // VALUES FROM CSV FILE TO NUMBERS
                    d.LATITUDE = +d.LATITUDE;
                    d.LONGITUDE = +d.LONGITUDE;

                    // CONVERT HEADCOUNT VALUES FROM
                    // CSV FILE TO NUMBERS
                    d.HEADCOUNT = +Math.round((d.HEADCOUNT) * 1) / 1;

                    // CALCULATE THE POSITION OF THE
                    // HEADCOUNT DATA POINT WITHIN
                    // THE PROJECTION
                    d.position = projection([
                        d.LONGITUDE, d.LATITUDE
                    ]);

                    return true;
                }
            });

            // DRAW THE CIRCLES ON THE MAP AND SIZE THEM
            // BASED ON HEADCOUNT
            // -----------------------------------------
            g.selectAll("circle")
                .data(data)
                .enter().append("circle")
                .attr("cx", function(d) {
                    return d.position[0];
                })
                .attr("cy", function(d) {
                    return d.position[1];
                })
                .attr("r", function(d) {
                    return (bubblescale(d.HEADCOUNT));
                })
                .attr("fill", "#b300b3")
                .attr("fill-opacity", "0.7")
                .on("mouseover", function(d) {
                    div.transition()
                        .duration(200)
                        .style("opacity", .9);
                    div.html(d.HEADCOUNT + " people in " + d.GEOCODE_LOCATION)
                        .style("left", (d3.event.pageX) + "px")
                        .style("top", (d3.event.pageY - 28) + "px");
                })
                .on("mouseout", function(d) {
                    div.transition()
                        .duration(500)
                        .style("opacity", 0);;
                });
        });

    });

    // ######################################################################
    // NESTING, FILTERING AND SUMMARIZING DATA
    // ######################################################################

    d3.csv("sample_data.csv", function(csv) {

        var data_nest = d3.nest()
            .key(function(d) {
                return d.GEOCODE_LOCATION;
            })
    .key(function(d) {
                return d.CATEGORY1;
            })
            .key(function(d) {
                return d.CATEGORY2;
            })
            .key(function(d) {
                return d.CATEGORY3;
            })
            .key(function(d) {
                return d.TYPE1;
            })
            .key(function(d) {
                return d.TYPE2;
            })
            .sortKeys(d3.ascending)
            .entries(csv);
        console.log(data_nest);

        var data_sums = d3.nest()
            .key(function(d) {
                return d.GEOCODE_LOCATION;
            })
    .key(function(d) {
                return d.LONGITUDE;
            })
    .key(function(d) {
                return d.LATITUDE;
            })
            .sortKeys(d3.ascending)
            .rollup(function(d) {
                return {
                    headcount: d3.sum(d, function(g) {
                        return +g.HEADCOUNT;
                    })
                };
            })
            .entries(csv);
        console.log(data_sums);

    });

    // ######################################################################
    // POPULATE THE FIVE MENU LISTS
    // ######################################################################

    function load_menus() {
        d3.csv("category1.csv", function(error, data_c1) {

            var select = d3.select("#c1_div")
                .append("div")
                .append("select")

            select
                .on("change", function(d) {
                    var selected_c1 = d3.select(this).property("value");
                    alert(value);
                });

            select.selectAll("option")
                .data(data_c1)
                .enter()
                .append("option")
                .attr("value", function(d) {
                    return d.x;
                })
                .text(function(d) {
                    return d.x;
                });
        });

        d3.csv("category2.csv", function(error, data_c2) {

            var select = d3.select("#c2_div")
                .append("div")
                .append("select")

            select
                .on("change", function(d) {
                    var selected_c2 = d3.select(this).property("value");
                    alert(value);
                });

            select.selectAll("option")
                .data(data_c2)
                .enter()
                .append("option")
                .attr("value", function(d) {
                    return d.x;
                })
                .text(function(d) {
                    return d.x;
                });
        });

        d3.csv("category3.csv", function(error, data_c3) {

            var select = d3.select("#c3_div")
                .append("div")
                .append("select")

            select
                .on("change", function(d) {
                    var selected_c3 = d3.select(this).property("value");
                    alert(value);
                });

            select.selectAll("option")
                .data(data_c3)
                .enter()
                .append("option")
                .attr("value", function(d) {
                    return d.x;
                })
                .text(function(d) {
                    return d.x;
                });
        });

        d3.csv("type1.csv", function(error, data_t1) {
            var select = d3.select("#t1_div")
                .append("div")
                .append("select")

            select
                .on("change", function(d) {
                    var selected_t1 = d3.select(this).property("value");
                    alert(value);
                });

            select.selectAll("option")
                .data(data_t1)
                .enter()
                .append("option")
                .attr("value", function(d) {
                    return d.x;
                })
                .text(function(d) {
                    return d.x;
                });
        });

        d3.csv("type2.csv", function(error, data_t2) {
            var select = d3.select("#t2_div")
                .append("div")
                .append("select")

            select
                .on("change", function(d) {
                    var selected_t2 = d3.select(this).property("value");
                    alert(value);
                });

            select.selectAll("option")
                .data(data_t2)
                .enter()
                .append("option")
                .attr("value", function(d) {
                    return d.x;
                })
                .text(function(d) {
                    return d.x;
                });
        });
    }

    window.onload = load_menus;

    // ######################################################################
    // FILTER DATA BASED ON USER SELECTION - NOT WORKING
    // ######################################################################

    function filter_data() {
        d3.csv("map_data.csv", function(error, data) {
            var filterCategory1 = selected_c1;
            var filterCategory2 = selected_c2;
            var filterCategory3 = selected_c3;
            var filterType1 = selected_t1;
            var filterType2 = selected_t2;

            var filtered_data = data.filter(function(d) {
                return d.CATEGORY1 == filterCategory1;
            })
        });
    }

这是我的示例数据的屏幕截图:

sample data

我已经在网上和书中搜索了一个多星期,但既没有找到我正在尝试做的事情的其他 D3 示例,也没有找到我的数据绑定、过滤和聚合问题的答案。

感谢任何帮助。我保证,当我有能力这样做时,我会把它付清。如果我能得到这个工作,我会很高兴 post 我的完整解决方案 github 和 link 从这个问题到它。

在我看来,您的问题不在于 D3,而是 JavaScript 范围。

当你写这样的东西时:

 d3.csv("category1.csv", function(error, data_c1) {

        var select = d3.select("#c1_div")
            .append("div")
            .append("select")

        select
            .on("change", function(d) {
                var selected_c1 = d3.select(this).property("value");
                alert(value);
            });

那个变量 selected_c1 只存在于包含它的匿名函数中。局部变量只能在定义它的函数内部使用。它对其他函数和其他脚本代码隐藏(请参阅此处:http://www.w3schools.com/js/js_function_closures.asp)。

因此,当您稍后尝试使用它时,在 filter_data() 中,它不可用。

此处的解决方案是将 selected_c1 和所有其他 4 个变量作为参数传递给 filter_data。可能是这样的:

 d3.csv("category1.csv", function(error, data_c1) {

        var select = d3.select("#c1_div")
            .append("div")
            .append("select")

        select
            .on("change", function(d) {
                var selected_c1 = d3.select(this).property("value");
                filter_data(selected_c1);
            });

然后,更改您的 filter_data() 参数:

function filter_data(selection) {
    d3.csv("map_data.csv", function(error, data) {

        var filtered_data = data.filter(function(d) {
            return d.CATEGORY1 == selection;
        })
    });
}

请记住,尽管您已成功将 selected_c1 传递给 filter_data(),但您现在再次面临同样的问题:var filtered_data 仅存在于 filter_data 中,并且您有将其传递给绘图函数的 2 个选项:

  1. 使其全球化;
  2. 将其作为参数传递。

这只是部分答案,您还需要付出一些努力才能将所有这些组合在一起。