I want to use d3 to plot some geoJSON on a leaflet map, however, the resultant paths are not being drawn with a great circle. How can I make the d3.geoPath do correct interpolation between points, similar to the standard geographic projections in d3?
Versus my old custom map:
Observable code:
D3VanAreasMap = {
let container = DOM.element('div', { style: `width:${width}px;height:${width/1.6}px` });
yield container;
let map = L.map(container).setView([0, 0], 1);
let osmLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
let geojson_linestring = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[-45, -45],
[45, -45],
]
},
"properties": {}
}]
}
//initialize svg to add to map
L.svg({clickable:true}).addTo(map) // we have to make the svg layer clickable
//Create selection using D3
const overlay = d3.select(map.getPanes().overlayPane)
const svg = overlay.select('svg').attr("pointer-events", "auto")
// create a group that is hidden during zooming
const g = svg.append('g').attr('class', 'leaflet-zoom-hide')
// Use Leaflets projection API for drawing svg path (creates a stream of projected points)
const projectPoint = function(x, y) {
const point = map.latLngToLayerPoint(new L.LatLng(y, x))
this.stream.point(point.x, point.y)
}
// Use d3's custom geo transform method to implement the above
const projection = d3.geoTransform({point: projectPoint})
// creates geopath from projected points (SVG)
const pathCreator = d3.geoPath().projection(projection)
const areaPaths = g.selectAll('path')
.data(geojson_linestring.features)
.join('path')
.attr('fill-opacity', 0.3)
.attr('stroke', 'black')
.attr("z-index", 3000)
.attr('stroke-width', 2.5)
.on("mouseover", function(d){
d3.select(this).attr("fill", "red")
})
.on("mouseout", function(d){
d3.select(this).attr("fill", "black")
})
// Function to place svg based on zoom
const onZoom = () => areaPaths.attr('d', pathCreator)
// initialize positioning
onZoom()
// reset whenever map is moved
map.on('zoomend', onZoom)
}

