D3.js
D3.js 支持 GeoJSON 、TopoJSON 、shapefiles 创建地图,同时支持大量的投影及交互效果。
参考文档
- https://observablehq.com/@d3/gallery (opens in a new tab)
- https://d3js.org/d3-geo (opens in a new tab)
- https://observablehq.com/collection/@d3/d3-geo-projection (opens in a new tab)
示例代码
<div id="container" style="width: 1000px;height: 800px"></div>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
//...
</script>
const colors = ["#2caffe", "#544fc5", "#00e272", "#fe6a35", "#6b8abc", "#d568fb", "#2ee0ca", "#fa4b42", "#feb56a", "#91e8e1"];
function renderFeature(features, projection, path) {
features.each(function(f, i) {
const featureEl = d3.select(this);
f.color = colors[i%colors.length];
featureEl.append('path')
.attr('d', path)
.attr('fill', f.color)
.attr('stroke', '#333')
.attr('stroke-width', 0.5)
.attr('opacity', 0.5);
let center = f.properties.center && projection(f.properties.center);
if(!center) return false;
featureEl.append('circle')
.attr('cx', center[0])
.attr('cy', center[1])
.attr('r', 3)
.attr('fill', '#444');
featureEl.append('text')
.attr('class', 'dataLabels')
.attr('text-anchor', 'middle')
.style('fill', '#333')
.style('font-size', '12px')
.text((d) => d.properties.name || '')
.attr('transform', (d) => {
if(!d.properties.name) return '';
let center = null;
let centroid = null;
if (d.properties.center) {
center = projection(d.properties.center);
}
if (d.properties.centroid) {
centroid = projection(d.properties.centroid);
}
if (!centroid) {
centroid = path.centroid(d);
}
if (!center) {
return `translate(${centroid.join(',')})`;
}
let distance = 20; // 10 pixels
let angle = Math.atan2(centroid[1] - center[1], centroid[0] - center[0]);
let newPoint = [center[0] + distance * Math.cos(angle), center[1] + distance * Math.sin(angle)];
return `translate(${newPoint.join(',')})`;
})
})
}
function createMap(el, mapdata) {
// 生成随机数据
const size = [
el.clientWidth,
el.clientHeight
];
const svg = d3.select(this.options.el)
.append('svg')
.attr('width', this.size[0])
.attr('height', this.size[1]);
const group = svg.append('g')
.attr('class', 'features');
const projection = d3.geoMercator().fitSize(size, mapdata);
const path = d3.geoPath().projection(projection);
const features = group.selectAll('g')
.data(mapdata.features)
.enter()
.append('g')
.attr('class', 'feature');
const zoom = d3.zoom()
.scaleExtent([0.25, 16])
.on('zoom', function (event) {
const isTranslationOnly = event.sourceEvent ? true : false;
if (isTranslationOnly ) {
group.attr('transform', event.transform); // 直接应用变换
} else {
group.transition() // 添加动画
.duration(300) // 设置动画持续时间
.attr('transform', event.transform); // 应用变换
}
});
renderFeature(features, projection, path);
svg.call(zoom)
}
fetch('https://geojson.cn/api/china/china.topo.json')
.then(response => response.json())
.then(mapdata => {
createMap(
document.querySelector('#container'),
mapdata
)
});