Before I go too far down the rabbit hole I noticed a rather bizarre rendering artifact in my TopoJSON that I am at a loss to explain. Rendered above is the official D3 distribution countries-110m.json
found on the TopoJSON World Atlas repository. As one can see, there is part of northern Russia that appears inverted left/right over northern Canada. Is there anything in my code which is causing this before I start putting together a plan B?
async function drawMap() {
map_obj = await getRAWMapData(); // Map topojson
const geojson_obj = topojson.feature(map_obj, map_obj.objects.countries);
var projection = d3.geoIdentity().fitWidth(width, geojson_obj).reflectY(true); // Map Projection
var path = d3.geoPath().projection(projection);
paths = SVGmap.selectAll('path').data(geojson_obj.features).enter().append('path');
paths
.attr('d', path)
.attr('fill', '#ddd')
.attr('stroke', 'white')
.attr('stroke-linejoin', 'round');
}
drawMap();
Problem
You are using spherical data and drawing it on a plane (with d3.geoIdentity) as though the data were planar.
Russia has part of its territory in the western hemisphere - the artifact is the path renderer stretching from the far East to the far West, and back again, as the path data crosses the anti-meridian when drawing Russia. The identity transform doesn't "know" that this is supposed to wrap behind a sphere - you're just taking latitude and longitude and stretching it across the screen as though it were 2D data.
D3.geoIdentity() fit methods only only manipulate the scale and translation of the data - there is no accounting for an anti-meridian or geographic projection of the data.
Solution
Use a projection - D3 geo projections use spherical math - they cut the features that cross the anti-meridian (in this case 180 degrees East/West). In this case Russia would consist of two path elements: one in the West and one in the East. This removes the artifact resulting from the stretching required to connect the two pieces in a single feature.
So, if you want to keep the plate carrée look, you could use:
var projection = d3.geoEquirectangular().fitWidth...
Any D3 projection will work, d3.geoMercator might be more familiar to some in terms of appearance.
The other solution would be to find preprojected geometry that is already cut across the anti-meridian - but this option is much less flexible than projecting the data with a D3 geo projection.