嗨,我正在创建 React 应用程序,在其中尝试使用 D3 制作世界地图。我希望当用户将鼠标悬停在特定国家/地区时,该国家/地区路径的质心上会出现圆圈,但对于“美国”、“法国”、“挪威”等某些国家/地区,质心有点偏离,因为这些国家/地区的部分地区彼此分开。
这是我的组件:
import styles from './WorldMap.module.css'
import * as d3 from "d3";
import {useEffect, useRef, useState} from "react";
import * as topojson from "topojson-client";
const WorldMap = () => {
const svgRef = useRef(null)
const pathRefs = useRef([])
let [mapJson, setMapJson] = useState(null)
let [mapTopo, setMapTopo] = useState(null)
let [tooltipCoordinates, setTooltipCoordinates] = useState([0,0])
useEffect(() => {
d3.json("https://unpkg.com/[email protected]/countries-50m.json").then(data => setMapTopo(data))
}, [])
useEffect(() => {
if (!mapTopo) return;
setMapJson(topojson.feature(mapTopo, mapTopo.objects.countries))
}, [mapTopo])
const projection = d3.geoMercator()
.center([0, 60])
const path = d3.geoPath(projection);
const handleMouseOver = (feature) => (e) => {
const coordinates = path.centroid(feature)
setTooltipCoordinates(coordinates)
}
return (
<svg className={styles.svg__map} ref={svgRef} viewBox={"0 0 900 400"}>
<g>
{mapJson?.features?.map((feature, i) => <path onMouseOver={handleMouseOver(feature)} className={styles.svg__country} ref={el => pathRefs.current[i] = el} key={i} d={d3.geoPath(projection)(feature)} />)}
<circle cx={tooltipCoordinates[0]} cy={tooltipCoordinates[1]} r={6} fill={"#2e2eca"} />
</g>
</svg>
)
}
export default WorldMap;
我正在以 topojson 形式获取国家/地区路径数据,然后将其转换为 json。
css文件:
.svg__map{
width: 100%;
min-height: 65vh;
background-color: #cbcccc;
}
.svg__country{
stroke-width: .5;
stroke: #949292;
}
是否有一些解决方案,或者可能有其他一些针对此类内容的地理投影?
我找到了一个解决方案,基本上你需要从一些包含每个国家/地区的纬度/经度值的服务中获取数据,并在该值上将投影应用于坐标,而不依赖于计算每个国家/地区形状的质心。
这篇文章可能更好地解释了这一点https://yangdanny97.github.io/blog/2019/08/24/D3-Mapmaking-Tips
我正在使用 Geojson 我的解决方案是获得国家最大的多边形
const getLargestPolygon = (geometry) => {
// If it's a MultiPolygon, find the largest polygon
if (geometry.type === "MultiPolygon") {
let largestArea = 0;
let largestPolygon = null;
geometry.coordinates.forEach((polygon) => {
const area = d3.geoArea({ type: "Polygon", coordinates: polygon });
if (area > largestArea) {
largestArea = area;
largestPolygon = polygon;
}
});
return { type: "Polygon", coordinates: largestPolygon };
} else {
// If it's already a Polygon, return it
return geometry;
}
};
const centroid = path.centroid(getLargestPolygon(d.geometry));