地圖投影
這裡使用的是 topoJSON 而非是 geoJSON 資料。網站也有許多網站能夠轉化檔案。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
path {
fill: #ccc;
stroke: #fff;
stroke-width: .5px;
}
.state:hover {
fill: red;
}
</style>
</head>
<body>
<script src="../d3.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script>
const width = 960,
height = 500
const path = d3.geoPath().projection(d3.geoAlbersUsa())
const svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
const g = svg.append('g').call(d3.zoom().scaleExtent([1, 10]).on('zoom', zoomHandle))
function zoomHandle() {
const transform = d3.event.transform
svg.select('g').attr('transform', `translate(${transform.x}, ${transform.y}) scale(${transform.k})`)
}
d3.json('./us.json').then(data => {
g.selectAll('path.state')
.data(topojson.feature(data, data.objects.states).features)
.enter()
.append('path')
.attr('class', 'state')
.attr('d', d => path(d))
})
</script>
</body>
</html>
複製程式碼
效果如下:
topoJSON 文件見:github.com/topojson/to…
我們先給 svg 定義了平移縮放,然後通過以下關鍵程式碼渲染了地圖:
const path = d3.geoPath().projection(d3.geoAlbersUsa())
g.selectAll('path.state')
.data(topojson.feature(data, data.objects.states).features)
.enter()
.append('path')
.attr('class', 'state')
.attr('d', d => path(d))
複製程式碼
我們定義了一個地圖的 path 生成器,通過 projection 方法設定生成器的投影方式。然後我們通過 topojson 拿到 地圖資料中各個州的資料,通過 path 元素和生成器渲染到頁面上。
d3 中常見的投影方式見下例。
投影方式
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
body {
background: #fcfcfa;
}
.map {
float: left;
margin: 20px;
text-align: center;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
</style>
</head>
<body>
<script src="../d3.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script>
const width = 300,
height = 300,
translate = [width / 2, height / 2],
projections = [
{name: 'geoAzimuthalEqualArea', fn: d3.geoAzimuthalEqualArea().scale(60).translate(translate)},
{name: 'geoAzimuthalEquidistant', fn: d3.geoAzimuthalEquidistant().scale(50).translate(translate)},
{name: 'geoGnomonic', fn: d3.geoGnomonic().scale(60).translate(translate)},
{name: 'geoOrthographic', fn: d3.geoOrthographic().scale(80).translate(translate)},
{name: 'geoStereographic', fn: d3.geoStereographic().scale(50).translate(translate)},
{name: 'geoAlbers', fn: d3.geoAlbers().scale(50).translate(translate)},
{name: 'geoConicConformal', fn: d3.geoConicConformal().scale(40).translate(translate)},
{name: 'geoConicEqualArea', fn: d3.geoConicEqualArea().scale(50).translate(translate)},
{name: 'geoConicEquidistant', fn: d3.geoConicEquidistant().scale(35).translate(translate)},
{name: 'geoEquirectangular', fn: d3.geoEquirectangular().scale(50).translate(translate)},
{name: 'geoMercator', fn: d3.geoMercator().scale(50).translate(translate)},
{name: 'geoTransverseMercator', fn: d3.geoTransverseMercator().scale(50).translate(translate)},
{name: 'geoNaturalEarth1', fn: d3.geoNaturalEarth1().scale(80).translate(translate)}
]
d3.json('./world-50m.json').then(data => {
projections.forEach(projection => {
const path = d3.geoPath().projection(projection.fn)
const div = d3.select('body')
.append('div')
.attr('class', 'map')
const svg = div.append('svg')
.attr('width', width)
.attr('height', height)
svg.append('path')
.datum(topojson.feature(data, data.objects.land))
.attr('class', 'land')
.attr('d', d => path(d))
svg.append('path')
.datum(topojson.mesh(data, data.objects.countries))
.attr('class', 'boundary')
.attr('d', path)
div.append('h3').text(projection.name)
})
})
</script>
</body>
</html>
複製程式碼
各投影方式的效果如下:
等值區域圖(熱度地圖)
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.states {
fill: none;
stroke: #fff;
stroke-width: .5px;
}
</style>
</head>
<body>
<script src="../d3.js"></script>
<script src="https://unpkg.com/topojson@3"></script>
<script>
const width = 960,
height = 600,
colors = d3.scaleLinear()
.domain([0.02, 0.04, 0.06, 0.08, 0.10])
.range(d3.schemeRdBu[6].reverse()),
path = d3.geoPath().projection(d3.geoAlbersUsa()),
svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height),
g = svg.append('g').call(
d3.zoom()
.scaleExtent([1, 10])
.on('zoom', zoomHandle)
)
function zoomHandle() {
const transform = d3.event.transform
g.attr('transform', `translate(${transform.x}, ${transform.y}) scale(${transform.k})`)
}
svg.append('text').text('2008年美國失業熱度圖').attr('transform', 'translate(400, 550)')
d3.json('./us.json').then(us => {
d3.tsv('./unemployment.tsv').then(unemployment => {
const map = new Map()
unemployment.forEach(d => {
map.set(Number(d.id), Number(d.rate))
})
g.append('g').selectAll('path')
.data(topojson.feature(us, us.objects.counties).features)
.enter()
.append('path')
.attr('d', d => path(d))
.attr('fill', d => colors(map.get(d.id)))
g.append('path')
.datum(topojson.mesh(us, us.objects.states))
.attr('class', 'states')
.attr('d', d => path(d))
})
})
</script>
</body>
</html>
複製程式碼
效果如下:
在請求到地圖資料和每個區域的失業值後,我們先是把失業資料置入一個 map 中,
const map = new Map()
unemployment.forEach(d => {
map.set(Number(d.id), Number(d.rate))
})
複製程式碼
這便於我們通過每個區域的 id 值找到其對應的失業率。
我們先通過
g.append('g').selectAll('path')
.data(topojson.feature(us, us.objects.counties).features)
.enter()
.append('path')
.attr('d', d => path(d))
.attr('fill', d => colors(map.get(d.id)))
複製程式碼
繪製出各個區,並以失業率值對應的顏色填充,然後再通過下面的程式碼繪製出各個州的輪廓:
g.append('path')
.datum(topojson.mesh(us, us.objects.states))
.attr('class', 'states')
.attr('d', d => path(d))
複製程式碼