Haste makes waste

Uda-DataAnalysis-54-机器学习[ing]-动画与互动

Posted on By lijun

5. 让我们制作地图

Let’s Make a Map,但是这个文章有些古老,这些新作Command-Line Cartography

地形(topography,地面的高度或形状)与拓扑结构(topology,在这种情况下与地点之间的邻接关系和连接有关)这两个词拼法很像,但它们是不同的。TopoJSON 对拓扑结构编码,不对地形进行编码。

地图学习

Check all of the following which are benefits of GeoJSON over Shapefiles.

  • can be parsed by most programming languages
  • human readable

7. 什么是投影?

image

image

Longitude values actually come first in GeoJSON. Draw a picture of latitude and longitude lines. As you move across lines of longitude the horizontal position changes. It’s similar to how the x-coordinate is listed before y-coordinates for points (x, y).

The mercator projection actually stretches areas near the poles. This is why Greenland and Antarctica appear so large in mercator projections.

mercator投影选择扭曲人最少的两极,即通过赤道将地球切开展平。

  • longitude:経度 Y
  • latitude:緯度 X
  • equator:赤道
  • Antarctica:南極大陸

image

  • 练习

image

8. 地图变形

image

  • 练习
  1. mercator投影产生非对称的扭曲,另外在经线上逐渐递增。
  2. mercator投影在纬度方向上,扭曲度是一样的。

image

9. D3中的地图

Ogre:将空间文件转换为 GeoJSON

如何将形状文件转换为可在 Github 上使用的 GeoJSON(作者:Ben Balter)

如果你想了解 GeoJSON 值如何转化为视觉表征,geojson.io 是一款交互式的 GeoJSON 编辑器。

下面的效果图如下: 02_globe_styled.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="http://d3js.org/d3.v3.min.js"></script>
    <style>
    </style>
    <script type="text/javascript">  
      function draw(geo_data) {
        "use strict";
        var margin = 75,
            width = 1400 - margin,
            height = 600 - margin;

        var svg = d3.select("body")
            .append("svg")
            .attr("width", width + margin)
            .attr("height", height + margin)
            .append('g')
            .attr('class', 'map');

        var projection = d3.geo.mercator()
                               .scale(150)
                               .translate( [width / 2, height / 1.5]);

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

        var map = svg.selectAll('path')
                     .data(geo_data.features)
                     .enter()
                     .append('path')
                     .attr('d', path)
                     .style('fill', 'lightBlue')
                     .style('stroke', 'black')
                     .style('stroke-width', 0.5);
      };
      </script>
  </head>
<body>
  <script type="text/javascript">
  /*
    Use D3 to load the GeoJSON file
    */
    
d3.json("world_countries.json", draw);
  </script>
</body>
</html>

14. 使用嵌套函数加载数据

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="http://d3js.org/d3.v3.min.js"></script>
    <style>
    </style>
    <script type="text/javascript">  
      function draw(geo_data) {
        "use strict";
        var margin = 75,
            width = 1400 - margin,
            height = 600 - margin;

        var svg = d3.select("body")
            .append("svg")
            .attr("width", width + margin)
            .attr("height", height + margin)
            .append('g')
            .attr('class', 'map');

        var projection = d3.geo.mercator()
                               .scale(140)
                               .translate([width / 2, height / 1.2]);
        
        var path = d3.geo.path().projection(projection);

        var map = svg.selectAll('path')
                     .data(geo_data.features)
                     .enter()
                     .append('path')
                     .attr('d', path)
                     .style('fill', 'lightBlue')
                     .style('stroke', 'black')
                     .style('stroke-width', 0.5);

        function plot_points(data) {
            //draw circles logic
        };

        var format = d3.time.format("%d-%m-%Y (%H:%M h)");

        d3.tsv("world_cup_geo.tsv", function(d) {
          d['attendance'] = +d['attendance'];
          d['date'] = format.parse(d['date']);
          return d;
        }, plot_points);
      };
      </script>
  </head>
<body>
  <script type="text/javascript">
  /*
    Use D3 to load the GeoJSON file
    */
    
d3.json("world_countries.json", draw);
  </script>
</body>
</html>

上面使用了嵌套函数来记载数据,顺序如下:

  1. 调用d3.json函数,加载json数据,加载完毕后调用draw函数
  2. 调用draw函数,在d3.tsv函数内,先加载tsv数据,加载完毕后,再调用plot_points函数

15. 嵌套函数

如需了解更多关于 D3 嵌套函数的信息,请查阅 D3 嵌套文档D3 嵌套示例

46. 世界杯代码和故事小结

22_globe_adding_events.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <script src="http://d3js.org/d3.v3.min.js"></script>
    <style>
      circle {
        fill: orange;
        stroke: black;
        stroke-width: 0.7;
        opacity: 0.7;
      }

      h2 {
        text-align: center;
        color: black;
      }

      div.years_buttons {
        position: fixed;
        top: 5px;
        left: 50px;
      }

      div.years_buttons div {
        background-color: rgb(251, 201, 127);
        padding: 3px;
        margin: 7px;
      }
    </style>
    <script type="text/javascript">  
      function draw(geo_data) {
        "use strict";
        var margin = 75,
            width = 1400 - margin,
            height = 600 - margin;

        d3.select("body")
          .append("h2")
          .text("World Cup ");

        var svg = d3.select("body")
            .append("svg")
            .attr("width", width + margin)
            .attr("height", height + margin)
            .append('g')
            .attr('class', 'map');

        var years = [];

          for(var i = 1930; i < 2015; i += 4) {
            if(i !== 1942 && i !== 1946) {
              years.push(i);
            };
          }

        var projection = d3.geo.mercator()
                               .scale(140)
                               .translate([width / 2, height / 1.2]);

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

        var map = svg.selectAll('path')
                     .data(geo_data.features)
                     .enter()
                     .append('path')
                     .attr('d', path)
                     .style('fill', 'lightBlue')
                     .style('stroke', 'black')
                     .style('stroke-width', 0.5);

        function plot_points(data) {
            
            function agg_year(leaves) {
                var total = d3.sum(leaves, function(d) {
                    return d['attendance'];
                });

                var coords = leaves.map(function(d) {
                    return projection([+d.long, +d.lat]);
                });

                var center_x = d3.mean(coords, function(d) {
                    return d[0];
                });

                var center_y = d3.mean(coords, function(d) {
                    return d[1];
                });

                var teams = d3.set();

                leaves.forEach(function(d) {
                    teams.add(d['team1']);
                    teams.add(d['team2']);
                });

                return {
                  'attendance' : total,
                  'x' : center_x,
                  'y' : center_y,
                  'teams' : teams.values()
                };
            }

            var nested = d3.nest()
                           .key(function(d) {
                              return d['date'].getUTCFullYear();
                           })
                           .rollup(agg_year)
                           .entries(data);

            var attendance_max = d3.max(nested, function(d) {
                return d.values['attendance'];
            });

            var radius = d3.scale.sqrt()
                           .domain([0, attendance_max])
                           .range([0, 15]);

            function key_func(d) {
                return d['key'];
            }

            svg.append('g')
               .attr("class", "bubble")
               .selectAll("circle")
               .data(nested.sort(function(a, b) { 
                  return b.values['attendance'] - a.values['attendance'];
               }), key_func)
               .enter()
               .append("circle")
               .attr('cx', function(d) { return d.values['x']; })
               .attr('cy', function(d) { return d.values['y']; })
               .attr('r', function(d) {
                    return radius(d.values['attendance']);
               })

          function update(year) {
              var filtered = nested.filter(function(d) {
                  return new Date(d['key']).getUTCFullYear() === year;
              });

              d3.select("h2")
                .text("World Cup " + year);

              var circles = svg.selectAll('circle')
                               .data(filtered, key_func);

              circles.exit().remove();

              circles.enter()
                     .append("circle")
                     .transition()
                     .duration(500)
                     .attr('cx', function(d) { return d.values['x']; })
                     .attr('cy', function(d) { return d.values['y']; })
                     .attr('r', function(d) {
                        return radius(d.values['attendance']);
                     });

              var countries = filtered[0].values['teams'];

              function update_countries(d) {
                  if(countries.indexOf(d.properties.name) !== -1) {
                      return "lightBlue";
                  } else {
                      return 'white';
                  }
              }

              svg.selectAll('path')
                 .transition()
                 .duration(500)
                 .style('fill', update_countries)
                 .style('stroke', update_countries);

          }

          var year_idx = 0;

          var year_interval = setInterval(function() {
            update(years[year_idx]);

            year_idx++;

            if(year_idx >= years.length) {
                clearInterval(year_interval);

                var buttons = d3.select("body")
                        .append("div")
                        .attr("class", "years_buttons")
                        .selectAll("div")
                        .data(years)
                        .enter()
                        .append("div")
                        .text(function(d) {
                            return d;
                        });

                buttons.on("click", function(d) {
                    d3.select(this)
                      .transition()
                      .duration(500)
                      .style("background", "lightBlue")
                      .style("color", "white");
                    update(d);
                });
            }
          }, 1000);
      }

      var format = d3.time.format("%d-%m-%Y (%H:%M h)");

      d3.tsv("world_cup_geo.tsv", function(d) {
        d['attendance'] = +d['attendance'];
        d['date'] = format.parse(d['date']);
        return d;
      }, plot_points);
    };
    </script>
  </head>
<body>
  <script type="text/javascript">
  /*
    Use D3 to load the GeoJSON file
    */
    
d3.json("world_countries.json", draw);
  </script>
</body>
</html>

44. 向按钮添加事件

Javascript 的 ‘this’ 清楚理解并掌握 JavasScript 的 ‘this’

如果你需要可以快速查阅的信息,此篇博文的部分内容向你提供了简洁明了的解释。

如需深入研究 JavaScript 的关键词 this,你可以就关键词 this 学习面向对象的 JavaScript 课程

Javascript 事件 D3.js 鼠标事件(作者:Anthony Nosek)

鼠标悬停、鼠标移出、鼠标按下教程(作者:Christophe Viau)

向 D3.js 图形添加工具提示

48. Matt 关于制作地图的提示

示例:

https://plot.ly/~etpinard/453/average-daily-surface-air-temperature-anomalies-c-in-july-2014-with-respect-to-1/

温度异常值是长期平均温度的差值。来源

视频 1:16 处,Matt 说:“一个是在散点图顶部的等高线图。”当把鼠标悬停在地图上时,散点图的方位即为呈现出来的坐标值。

https://plot.ly/~MattSundquist/878/the-1000-most-populous-canadian-cities/

D3 资源

其他资源

视频 2:13 处,Matt 提到用来制作地图的 GUI。图形用户界面 (Graphical User Interface) 或 GUI 是一款点击式软件,是命令行界面的替代方案。Tableau 和 Data Wrapper 是 Matt 提到的两款 GUI。

Tableau Data Wrapper

50. 再见,继续学习吧