D3.js制作带悬浮提示框的渐变色中国地图(使用node.js提供服务)

一.效果图

在这里插入图片描述

使用D3来制作中国地图主要有几个地方需要注意:

  1. 需要用到投影函数,并挂在在路径生成器上。
  2. 由于同源策略限制的原因,需要通过服务器来返回地图文件,比如china.json这种。
  3. 如果需要做渐变色渲染或者显示标注,需要额外的数据,并通过服务器返回。
  4. 要区分开topojsongeojson两种格式的数据的不同,他们的加载模式也有所不同,相对于geojson数据,topojson文件更小,渲染时更节省Dom空间。

二.代码示例

我把代码部分分为前端和后端,咱们先看前端的部分。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>topojsonMAP</title>
    <style>
        /* Tooltip CSS */
        .d3-tip {
            line-height: 1.5;
            font-weight: 400;
            font-family: "avenir next", Arial, sans-serif;
            padding: 6px;
            background: rgba(0, 0, 0, 0.6);
            color: #FFA500;
            border-radius: 1px;
            pointer-events: none;
        }

        /* Creates a small triangle extender for the tooltip */
        .d3-tip:after {
            box-sizing: border-box;
            display: inline;
            font-size: 8px;
            width: 100%;
            line-height: 1.5;
            color: rgba(0, 0, 0, 0.6);
            position: absolute;
            pointer-events: none;

        }

      
        .d3-tip.n:after {
            content: "\25BC";
            margin: -1px 0 0 0;
            top: 100%;
            left: 0;
            text-align: center;
        }       
    </style>
</head>

<body>

</body>

</html>
<script src="/static/js/d3-v4.js"></script>  
<script src="/static/js/topojson.js"></script>  <!-- 用来处理topojson格式的地图文件 -->
<script src="/static/js/d3-tip.js"></script>	<!-- 用来生成tip提示框 -->
<script>
    const width = 1200;
    const height = 1000;
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g");

    var tip = d3.tip()
        .attr('class', 'd3-tip')
        .offset([-10, 0])
        .html((d) => {
            return "<strong>Country: </strong><span class='details'>" + d.properties.name + "<br></span>"
        });

    //创建投影函数
    var projection = d3.geoMercator()
        .center([107, 31])
        .scale(950)
        .translate([width / 2, height / 2 + height / 8])


    //创建地理路径生成器
    var path = d3.geoPath()
        .projection(projection);


    svg.call(tip);
    //读取json文件并创建地图
    d3.json("/static/china.topojson", (error, topoRoot) => {
        if (error)
            return console.error(error);

        //将TopoJSON对象转换成GeoJSON,保存在georoot中
        var geoRoot = topojson.feature(topoRoot, topoRoot.objects.china);
        //输出GeoJSON对象
        console.log('geoRoot', geoRoot);

        //添加中国各种的路径元素
        var provinces = svg.append("g")
            .attr("class", "countries")
            .selectAll("path")
            .data(geoRoot.features)
            .enter()
            .append("path")
            .attr("class", "province")
            .style("fill", "#ccc")
            .attr("d", path)
            .on("mouseover", (d) => {
                tip.show(d);
                d3.select(this)
                    .style("opacity", 0.8)
            })
            .on("mouseout", (d) => {
                tip.hide(d);
                d3.select(this)
                    .style("opacity", 1)
            });

        var $ = {
            ajax: (url) => {
                var request = new XMLHttpRequest();
                request.open('get', url);
                request.send();
                request.onreadystatechange = () => {
                    if (request.readyState === 4 && request.status === 200) {
                        var json = JSON.parse(request.responseText);
                        //将读取到的数据存到数组values,令其索引号为各省的名称
                        var cityArr = [];
                        for (var i = 0; i < json.provinces.length; i++) {
                            var name = json.provinces[i].name;
                            var value = json.provinces[i].value;
                            cityArr[name] = value;
                        }

                        //求最大值和最小值
                        var maxvalue = d3.max(json.provinces, function (d) {
                            return d.value;
                        });
                        var minvalue = 0;

                        //定义一个线性比例尺,将最小值和最大值之间的值映射到[0, 1]
                        var linear = d3.scaleLinear()
                            .domain([minvalue, maxvalue])
                            .range([0, 1]);

                        //创建颜色插值函数
                         var colorFunc = d3.interpolate("#395988", "#4D8DC3");

                        //设定各省份的填充色
                        provinces.style("fill", function (d, i) {
                            var t = linear(cityArr[d.properties.name]);
                            var color = colorFunc(t);
                            return color.toString();
                        })                
                    }
                }
            }
        }
        $.ajax('/stats/data');
    });
</script>

我在上述代码中已经添加了很清晰的注释,但是需要注意的仍然有几点:

  1. d3-tip.js这个文件大家在github上下载的时候,如果使用最新版本的话以上代码是会报错的,如果你严格按照github上这个项目作者所提示的一样去引用,不好意思,还是会报错,但是你如果换成2013这个版本就可以运行起来,如下图版本:
    在这里插入图片描述

  2. d3.json()这个高阶函数并不是必须要使用的。你们可以在上面看到,我用原生js封装了一个ajax函数$.ajax()用来处理get请求,也能得到相应数据,当然,你如果通过服务器获取数据,记住要将它格式化成json格式的数据。

  3. TopoJSONGeoJSON 按拓扑学编码后的扩展形式,是由 D3 的作者 Mike Bostock 制定的。相比 GeoJSON直接使用 Polygon、Point 之类的几何体来表示图形的方法,TopoJSON中的每一个几何体都是通过将共享边(被称为arcs)整合后组成的。TopoJSON消除了冗余,文件大小缩小了 80%,因为:1.边界线只记录一次(例如广西和广东的交界线只记录一次)2.地理座标使用整数,不使用浮点数。

下面我们看看后端部分,后端由node.js提供服务器。

const express = require('express');
const path = require('path')

var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

/*返回静态资源 public是我存放静态资源的文件夹,static是我准备返回静态资源的url*/
app.use('/static', express.static(path.join('public')));

app.get('/map/faster', (req, res) => {
    res.render('map_faster');
});

app.get('/stats/data', (req, res) => {
    const data = {
        "name": "中国",
        "provinces": [{
                "name": "北京",
                "value": 14149
            },
            {
                "name": "天津",
                "value": 2226.41
            },
            {
                "name": "河北",
                "value": 1544.94
            },
            {
                "name": "山西",
                "value": 3720.24
            },
            {
                "name": "内蒙古",
                "value": 2771.96
            },
            {
                "name": "辽宁",
                "value": 6263.69
            },
            {
                "name": "吉林",
                "value": 4494.77
            },
            {
                "name": "黑龙江",
                "value": 3835.48
            },
            {
                "name": "上海",
                "value": 5493.23
            },
            {
                "name": "江苏",
                "value": 12299.72
            },
            {
                "name": "浙江",
                "value": 14151.74
            },
            {
                "name": "安徽",
                "value": 1562.67
            },
            {
                "name": "福建",
                "value": 14225.67
            },
            {
                "name": "江西",
                "value": 384.73
            },
            {
                "name": "山东",
                "value": 9923.65
            },
            {
                "name": "河南",
                "value": 1611.41
            },
            {
                "name": "湖北",
                "value": 1202.97
            },
            {
                "name": "湖南",
                "value": 928.36
            },
            {
                "name": "广东",
                "value": 15610.67
            },
            {
                "name": "广西",
                "value": 9278.87
            },
            {
                "name": "海南",
                "value": 13348.02
            },
            {
                "name": "重庆",
                "value": 1168.32
            },
            {
                "name": "四川",
                "value": 7798.15
            },
            {
                "name": "贵州",
                "value": 168.94
            },
            {
                "name": "云南",
                "value": 8947.08
            },
            {
                "name": "西藏",
                "value": 13405.7
            },
            {
                "name": "陕西",
                "value": 1597.47
            },
            {
                "name": "甘肃",
                "value": 4522.35
            },
            {
                "name": "青海",
                "value": 5424.32
            },
            {
                "name": "宁夏",
                "value": 545.45
            },
            {
                "name": "新疆",
                "value": 13150.57
            }
        ]
    }
    res.send(data);
})

var server = app.listen('7002', function (req, res) {
    var port = server.address().port;
    console.log('Start with port: ' + port);
})

至此,整个绘制就完成了。如果大家有疑问,欢迎在底下留言。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章