简体   繁体   English

使用单选按钮过滤数据(动画地图)

[英]Filtering data with radio button (animated map)

I have animated slider map that takes lat/long data and displays it based on when a type of building has been erected. 我有一个动画的滑块地图,该地图可以获取经/纬度数据,并根据何时搭建的建筑物类型进行显示。 Currently, I am only able to display data for skyscrapers, however I would like each of the buttons to filter data for "residential" and "hospitals" as well. 目前,我只能显示摩天大楼的数据,但是我希望每个按钮也可以过滤“住宅”和“医院”的数据。

d.skyscrp is what is pulling the date data from data5001.txt, and then moment() is parsing the data as information to be read by the browser. d.skyscrp是从data5001.txt中提取日期数据,然后moment()将数据解析为要由浏览器读取的信息。 I need to access d.residential and d.hospitals as well. 我还需要访问d.residential和d.hospitals。 So far I am able to load the data, but do not know how to make the buttons filter out what i need. 到目前为止,我已经能够加载数据,但是不知道如何使按钮过滤掉我所需的内容。

I have a full working example here on bl.ocks with all the code available. 我在bl.ocks上有一个完整的工作示例 ,其中提供了所有可用代码。

Plunker of code located here 位于此处的代码笨拙

在此处输入图片说明

Index.html below as well for reference: 下面还有Index.html供参考:

<!DOCTYPE html>
<head>
    <title>Map</title>
<meta charset="utf-8">
<link rel="stylesheet" href="d3.slider.css"/>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.3/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="d3.slider.js"></script>

</head>

<body>
        <div id="update">
            <div id="agegrp" class="buttons">
                            <div class="button current" data-val="1" onclick = "updateData()" id = "option" name="updateButton"
                               type="button" value = "Update">Button1</div>
                            <div class="button" data-val="2">Button2</div>
                            <div class="button" data-val="3">Button3</div>
                            <div class="button" data-val="4" style="margin-right:0">Button4</div>
                <div class="clr"></div>
            </div>
        </div><!-- @end #update -->

    <div id="title">
        </div>

    <div id="subtitle">
        </div>

        <div id="axis1985">
            <h8></h8>
            </div>

        <div id="axis1990">
            <h8>1990</h8>
            </div>

            <div id="axis1995">
                <h8>1995</h8>
                </div>

                <div id="axis2000">
                    <h8>2000</h8>
                    </div>

                    <div id="axis2005">
                        <h8>2005</h8>
                        </div>

                        <div id="axis2010">
                            <h8>2010</h8>
                            </div>

                            <div id="axis2015">
                                <h8>2015</h8>
                                </div>

    <div id="slider3">
        </div>

        <div id="slidertextplaceholder"></div>
        <div id="slidertext"></div>


        <div id="option">
    <input name="updateButton"
           type="button"
           value="Update"
           onclick="updateData()" />
</div>

<script>

var width = 960,
    height = 850;

var mapPath = "usa.json";

// Define the div for the tooltip
var div = d3.select("body").append("div")
    .attr("class", "tooltip")
    .style("opacity", 0);

var projection = d3.geo.albersUsa()
    .scale(1080)
    .translate([width / 2, height / 2]);

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

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json(mapPath, function(error, us) {
  if (error) return console.error(error);

  svg.append("path")
      .datum(topojson.feature(us, us.objects.land))
      .attr("d", path)
      .attr("class", "land-boundary");

  svg.append("path")
      .datum(topojson.mesh(us, us.objects.counties, function(a, b) { return a !== b && (a.id / 1000 | 0) === (b.id / 1000 | 0); }))
      .attr("d", path)
      .attr("class", "county-boundary");

  svg.append("path")
      .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
      .attr("d", path)
      .attr("class", "state-boundary");


  d3.tsv("data5001.txt")
    .row(function(d) {
      return {
        permalink: d.permalink,
        lat: parseFloat(d.lat),
        lng: parseFloat(d.lon),
        state: d.state,
        fullAddress: d.fullAddress,
        buildingName: d.buildingName,
        skyscrp: moment(d.skyscrp,"YYYY-MM-DD HH:mm"),
        residential: moment(d.residential,"YYYY-MM-DD HH:mm"),
        hospital: moment(d.hospital,"YYYY-MM-DD HH:mm")
            };
    })
    .get(function(err, rows) {
        if (err) return console.error(err);
      window.site_data = rows;
    });
});



//display the sites using "permalink"
var displaySites = function(data) {
  var sites = svg.selectAll(".site")
      .data(data, function(d) {
        return d.permalink;
      });

  sites.enter().append("circle")
      .attr("class", "site")
      .attr("cx", function(d) {
        return projection([d.lng, d.lat])[0];
                        })
      .attr("cy", function(d) {
        return projection([d.lng, d.lat])[1];
            })
                 .on("mouseover", function(d) {
                 div.transition()
                         .duration(200)
                         .style("opacity", .9);
                 div.html("building Name:" + "<br>" + d.buildingName + "<br>" +  "<br>" + "Address:" + "<br>" + d.fullAddress + "<br>")
                        .style("left", (d3.event.pageX) + "px")
                        .style("top", (d3.event.pageY - 28) + "px");
                 })
             .on("mouseout", function(d) {
                 div.transition()
                        .duration(200)
                      .style("opacity", 0);
            })
  .attr("r", 1)
  .transition().duration(800)
    .attr("r", 7);

  sites.exit()
    .transition().duration(200)
      .attr("r",0)
      .remove();
};

var minDateYear = moment('1985-12-19', "YYYY-MM-DD HH:mm");
var maxDateYear = moment('2017-09-29', "YYYY-MM-DD HH:mm");
var secondsInDay = 60 * 60 * 24;

d3.select('#slider3').call(d3.slider()
  //.axis(true).min("1986").max("2017")
    .axis(false).min(minDateYear).max(maxDateYear)
  .on("slide", function(evt, value) {
    var newData = _(site_data).filter( function(site) {
      return site.skyscrp < value;
    })
    //console.log("New set size ", newData.length);
     //console.log("svg value ", newData);
     document.getElementById("slidertext").innerHTML = "Achieved Goals:  " + newData.length;
    displaySites(newData);
  })
);

document.getElementById("slidertextplaceholder").innerHTML = "Achieved Goals:  " + " ____ " + "buildings";
</script>
</body>

You just need to pull your filter out into shared code. 您只需要将过滤器放入共享代码即可。

Here is how I would go about it: 这是我的处理方法:

// First, a variable to keep the year we're filtering on.
var yearMin = 0;
// Another one to keep track of the building type we're filtering on.
var buildingType = 'skyscrp';

// Shared function
function filterData() {
  var newData = _(site_data).filter(function(site) {
    return site[buildingType] < minYear;
  });
  displaySites(newData);
  document.getElementById("slidertext").innerHTML =
      "Achieved Goals:  " + newData.length;
}

I've made a few assumptions, but the basic idea is I filter on year and also on building type in the same statement. 我做了一些假设,但基本思路是我在同一条语句中根据年份和建筑类型进行过滤。

So each of the buttons will set buildingType to an appropriate value. 因此,每个按钮都会将buildingType设置为适当的值。 After setting the value of buildingType , they call filterData(); 设置buildingType的值后,他们调用filterData(); .

function buttonClick(filterField) {
  if (filterField !== undefined) {
    console.info('Changing building filter to', filterField);
    buildingType = filterField;
  }
  console.info('Applying filter');
  filterData();
}

Then, for the slider, you modify the code like this: 然后,对于滑块,您可以如下修改代码:

d3.select('#slider3').call(d3.slider()
  .axis(false).min(minDateYear).max(maxDateYear)
  .on("slide", function(evt, value) {
    console.info('Changing year filter to', value);
    minYear = value;
    filterData();
  })
);

Edit 编辑

Looking at the data, it seemed to me more likely that you want to filter on a different field, depending on the selected button. 查看数据,在我看来,您更有可能希望根据所选按钮在其他字段上进行过滤。 For example, right now you're filtering on skyscrp , but there are also hospital and residential fields. 例如,现在您正在对skyscrp过滤,但也有hospitalresidential

So, using the same strategy as before, I just altered the filterData function, yielding a working plunkr . 因此,使用与以前相同的策略,我只是更改了filterData函数,产生了一个有效的plunkr

These are the changes I made, reflected in the code above: 这些是我所做的更改,反映在上面的代码中:

  1. Added an onclick handler to your first three buttons. 在前三个按钮中添加了一个onclick处理程序。 Each button sends through one of the three field names 'skyscrp', 'hospital' or 'residential'. 每个按钮通过“ skyscrp”,“ hospital”或“ residential”三个字段名称之一发送。 I also changed their titles to match the filters they apply, but you'll want to do something extra to update the styling on the screen to show which filter is active - I don't think that falls in the scope of your question. 我还更改了标题以匹配其应用的过滤器,但是您将需要做一些额外的工作来更新屏幕上的样式,以显示哪个过滤器处于活动状态-我认为这不在您的问题范围之内。
  2. Added the buttonClick function which, as explained before, simply sets the filter and calls filterData() . 如前所述,添加了buttonClick函数,该函数只需设置过滤器并调用filterData() But instead of a number representing the building type, I now use the actual field name in your data. 但是,我现在不再使用代表建筑物类型的数字,而是使用数据中的实际字段名称。
  3. Updated filterData to now use that field for the comparison. 更新了filterData ,现在使用该字段进行比较。

Edit 2 编辑2

Updating the label now happens within filterData , which gives a more consistent experience. 现在,更新标签发生在filterData内,从而提供了更加一致的体验。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM