Hi I am creating React app in which I am trying to make world map using D3. I want when user hovers on specific country to appear circle on centroid of that country path, but for some countries like "USA", "France", "Norway" centroid is a bit off because parts of those countries are separated from one another.
Here is my component:
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/world-atlas@2.0.2/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;
I am fetching countries path data as topojson then converting it to json.
css file:
.svg__map{
width: 100%;
min-height: 65vh;
background-color: #cbcccc;
}
.svg__country{
stroke-width: .5;
stroke: #949292;
}
This is screenshot of centeroid for norway:
Is there some fix for this, or maybe some other geo Projection for this kind of stuff?
I found a solution, basically you need to fetch data from some service that contains lat/long values for each country and on that values apply the projection to the coordinates , without relying on calculating centroid for each country's shape.
This is maybe better explained in this article https://yangdanny97.github.io/blog/2019/08/24/D3-Mapmaking-Tips