【问题标题】:Implementing multiple filters on same dataset in d3.js在 d3.js 中对同一数据集实现多个过滤器
【发布时间】:2018-11-17 07:24:14
【问题描述】:

我希望就如何在我拥有的 d3/传单地图上的同一数据集(但有两个不同的属性)上实现多个范围滑块过滤器获得一些建议。

我希望能够使用范围滑块按日期过滤地图上的点,并使用另一个范围滑块按“大小”过滤地图上的点。目前,我进行此设置的方式仅适用于按日期过滤。本质上,当时间滑块被实例化时,它从一个函数传入一个不透明度值,该函数可以动态地将点的不透明度设置为零,从而将它们过滤掉。这是基本代码:

//Build time-slider in HTML
<div  id="sliderContainer"> 
<input id="timeslide-leaflet" type="range" min="0" max="25" value="0" step="1" /><br>

//Define years for time slider
var inputValue = null;
var years = ["1993","1994","","","" ];

        //load in data
        SFData.forEach(function(d) {
        var coords = d.geometry.coordinates
        console.log(coords)
        d.latLong = new L.LatLng(coords[1],
                                coords[0]);

    })

    //draw map circles
    var feature = mapG.selectAll("circle")
        .data(SFData)
        .enter().append("circle")
        .attr("class", 'features')

    //if time-slider moved, call update function
    d3.select("#timeslide-leaflet").on("input", function() {        
        update(+this.value);   
    });

    //Update function sets feature's opacity and can filter by making invisible
    function update(value) {
        document.getElementById("range-leaflet").innerHTML=years[value];
        inputValue = years[value];

        d3.selectAll(".features")
            .attr("opacity", dateMatch)       
}


    function dateMatch(data, value) {

    //do some internal calculations based on my data

    if (yearInt <= inputValueInt && yearExpirationInt >= inputValueInt) {

        return ".7";
    } else {
        return "0"; //return opacity 0 for data that should be filtered out 
    };
}

我希望能够以类似的方式过滤不同属性(大小)上的相同数据。我可以轻松地创建另一个时间滑块 -​​ 但我将如何将另一个返回的不透明度值传递给地图特征 .attr("opacity")?本质上,我可以编写一个与 dateMatch() 类似的按大小过滤的函数,我不确定如何将返回的不透明度值传递回地图特征,因为 dateMatch() 已经传入了一个值。

谢谢!

【问题讨论】:

    标签: javascript html d3.js leaflet


    【解决方案1】:

    制作一个链接不透明度的函数

    d3.selectAll(".features")
        .attr("opacity", calcOpacity);
    
    function calcOpacity(d) {
        var opacity = dateOpacity(d, 1);
        return c2Opacity(d, opacity);
    }
    
    function dateOpacity(d, opacity) {
        if (opacity === 0.0) return opacity;
        // calcs
        return (yearInt <= inputValueInt && yearExpirationInt >= inputValueInt) ? 0.7 : 0.0;
    }
    
    function c2Opacity(d, opacity) {
        if (opacity === 0.0) return opacity;
        // calcs
        return Math.random() < 0.5 ? 0.7 : 0.0;
    }
    

    编辑

    我已经删除了此答案的 DiamondJoe12 编辑并放置了一个 sn-p,它使用 2 个滑块来过滤图表中的圆圈。

    滑块提供了所需的yearsize 值,我看不出使用查找数组的理由。

    滑块的最低值用作标记​​以通过该特定过滤器。

    sn-p 显示一个包含 500 个不同大小的圆圈和不同年份数据的 svg。

    var svgWidth = 500, svgHeight = 500;
    
    var svg = d3.select("body")
      .append("svg")
        .attr("width", svgWidth)
        .attr("height", svgHeight);
    
    var color = d3.scaleOrdinal(d3.schemeCategory10);
    var x = d3.scaleLinear().domain([-0.1, 1.1]).range([0, svgWidth]);
    var y = d3.scaleLinear().domain([-0.1, 1.1]).range([0, svgHeight]);
    
    var data = d3.range(500).map(i => ({x:Math.random(), y:Math.random(),
        year: 1993 + Math.floor(Math.random()*26),
        size: 4 + Math.floor(Math.random()*7) }));
    
    svg.selectAll(".features")
        .data(data)
      .enter()
      .append("circle")
        .attr("class", "features")
        .attr("cx", d => x(d.x))
        .attr("cy", d => y(d.y))
        .attr("r", d => d.size)
        .attr("fill", (d, i) => color(i));
    
    //if time-slider moved, call update function
    d3.select("#timeslide-leaflet").on("input", function() {
        update();
    });
    
    d3.select("#sizeslide-leaflet").on("input", function() {
        update();
    });
    
    update();
    
    //Update function sets feature's opacity and can filter by making invisible
    function update() {
        var year = +document.getElementById("timeslide-leaflet").value;
        year = year === 1992 ? 0 : year;
        document.getElementById("time-leaflet").innerHTML = year === 0 ? "All" : year;
    
        var size = +document.getElementById("sizeslide-leaflet").value;
        size = size === 3 ? 0 : size;
        document.getElementById("size-leaflet").innerHTML = size === 0 ? "All" : size;
    
        svg.selectAll(".features")
          .transition()
          .duration(200)
            .attr("opacity", d => calcOpacity(d, year, size) );
    }
    
    function calcOpacity(d, year, size) {
        var opacity = dateOpacity(d, year, 1);
        return sizeOpacity(d, size, opacity);
    }
    
    function dateOpacity(d, year, opacity) {
        if (opacity === 0.0) return opacity;
        if (year === 0)  return opacity; // All pass
        return (d.year === year) ? 0.7 : 0.0;
    }
    
    function sizeOpacity(d, size, opacity) {
        if (opacity === 0.0) return opacity;
        if (size === 0)  return opacity; // All pass
        return d.size === size ? 0.7 : 0.0;
    }
    .features {
    stroke:black;
    stroke-width:0.5;
    }
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <div id="sliderContainer"> 
    <input id="timeslide-leaflet" type="range" min="1992" max="2018" value="1992" step="1" /><br/>
    <input id="sizeslide-leaflet" type="range" min="3" max="10" value="3" step="1" />
    </div>
    <div>
    <div>Year: <span id="time-leaflet"></span></div>
    <div>Size: <span id="size-leaflet"></span></div>
    </div>

    【讨论】:

    • 感谢您的回答。不知道为什么它被否决了。虽然我认为我看到了这里的逻辑,但我最终实现了一种效率较低但更直接的方法。它有很多重复的代码,但它确实有效。我基本上只是复制了滑块,并添加了条件,以便如果尺寸滑块满足“大小”和“日期”条件,它将相应地调整不透明度。这样,日期和时间滑块协同工作,我可以同时过滤大小/日期。
    • @DiamondJoe12:如果您在更新函数中读取两个滑块的值并在两个滑块更改时调用更新函数,则不需要重复代码
    • rio- 见上文。我正在遵循您的建议-但我似乎无法在上面显示的 d3.select().on("input()) 函数中传递两个滑块的值。我想我很接近但还有一些我做的不对。
    • @DiamondJoe12:看看例子
    猜你喜欢
    • 2020-03-02
    • 1970-01-01
    • 2020-03-18
    • 2013-09-05
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多