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);
})

至此,整個繪製就完成了。如果大家有疑問,歡迎在底下留言。

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