I'm trying to make an interactive page in which there's a world map, and when you click on a location it creates a marker and saving the coordinates. Any idea on how to achieve that?
Asked
Active
Viewed 2,550 times
1 Answers
1
After spending a weekend looking for a solution, this should solve the problem. Works with most events too, not just click.
import React, { useState } from "react";
import {
ComposableMap,
Geographies,
Geography,
Marker,
ZoomableGroup
} from "react-simple-maps";
import { geoPath } from "d3-geo";
const geoUrl =
"https://raw.githubusercontent.com/zcreativelabs/react-simple-maps/master/topojson-maps/world-110m.json";
const markersBegin = [
{
markerOffset: -30,
name: "Buenos Aires",
coordinates: [-58.3816, -34.6037]
}
];
const MapChart = () => {
const [scale, setSclae] = useState(1);
const [markers, setMarkers] = useState(markersBegin);
return (
<ComposableMap projection="geoMercator">
<ZoomableGroup zoom={1} onMoveEnd={({ zoom }) => setSclae(zoom)}>
<Geographies geography={geoUrl}>
{({ geographies, projection }) =>
geographies
.filter((d) => d.properties.REGION_UN === "Americas")
.map((geo) => {
const onGeoEventFactory = (handleCoordinates) => {
const gPath = geoPath().projection(projection);
return (evt) => {
const dim = evt.target.getBoundingClientRect();
const cx = evt.clientX - dim.left;
const cy = evt.clientY - dim.top;
const [geoX, geoY] = gPath.bounds(geo)[0];
//we need root SVG element of our map
const svg = evt.nativeEvent.path[4];
//adjust for SVG width on the page / internal rendering width
const adjustScale =
scale * (svg.clientWidth / svg.viewBox.baseVal.width);
// these are the coords in SVG coordinate system
const clickCoordsInsideSvg = [
geoX + (cx / adjustScale),
geoY + (cy / adjustScale)
];
// 'unproject' the SVG coords to get lat and long
handleCoordinates(projection.invert(clickCoordsInsideSvg));
};
};
return (
<Geography
key={geo.rsmKey}
geography={geo}
fill="#EAEAEC"
stroke="#D6D6DA"
onClick={onGeoEventFactory((coordinates) => {
const newMarkers = [
...markers,
{
markerOffset: -30,
name: "Marker",
coordinates
}
];
setMarkers(newMarkers);
})}
/>
);
})
}
</Geographies>
{markers.map(({ name, coordinates, markerOffset }) => (
<Marker key={name} coordinates={coordinates}>
<g
fill="none"
stroke="#FF5533"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
transform="translate(-12, -24)"
>
<circle cx="12" cy="10" r="3" />
<path d="M12 21.7C17.3 17 20 13 20 10a8 8 0 1 0-16 0c0 3 2.7 6.9 8 11.7z" />
</g>
<text
textAnchor="middle"
y={markerOffset}
style={{ fontFamily: "system-ui", fill: "#5D5A6D" }}
>
{name}
</text>
</Marker>
))}
</ZoomableGroup>
</ComposableMap>
);
};
export default MapChart;

Ivan Koshelev
- 3,830
- 2
- 30
- 50
-
There seems to be an error on the line: `const svg = evt.nativeEvent.path[4];` Is there a fix for this? – cbutler Mar 16 '23 at 16:45
-
It's possible that something changed in the library. You should pause in `debugger` and inspect native event. You need to find the reference to the root SVG element of your map. – Ivan Koshelev Mar 16 '23 at 23:44
-
Thanks. It turns out I don't need that part for my use case. – cbutler Mar 17 '23 at 12:56