Search code examples
javascriptd3.jsmap-projections

D3.js Rotating a map breaks styling


I am trying to rotate a globe (orthographic projection). What I have now does rotate the globe, though it is very choppy, and it breaks how the map looks after i drag it (graticules and ocean fill)

How can I improve my code to make it better? Here is the relevant code:

const svg = d3.select('svg');

const projection = d3.geoOrthographic()
const graticule = d3.geoGraticule();
let pathGenerator = d3.geoPath().projection(projection);


const g = svg.append('g');

g.append('path')
    .attr('class', 'sphere')
    .attr('d', pathGenerator({type: 'Sphere'}));

g.append('path')
    .datum(graticule)
    .attr("class", "graticule")
    .attr("d", pathGenerator);

g.call(d3.drag()
    .on("start", dragstarted)
    .on("drag", dragged)
    .on("end", dragended));


g.call(d3.zoom().on('zoom', () => {
   g.attr('transform', d3.event.transform)
 }));

function dragstarted(){
  console.log("started");
}
function dragged(){
  const rotate = projection.rotate()
    const k = 75 / projection.scale()
    //console.log(k);
    projection.rotate([
      rotate[0] + d3.event.dx * k,
      rotate[1] - d3.event.dy * k
    ])
    pathGenerator = d3.geoPath().projection(projection)
    svg.selectAll("path").attr("d", pathGenerator)
}
function dragended(){
  console.log("drag ended");
}

edit: Live demo: https://vizhub.com/Glebenator/f44ac266b14f4c92b88113fcc89c389d?edit=files&file=index.html


Solution

  • Okay so I did two things.

    1. Inside of the dragged function instead of selecting all the path elements as one I selected them individually... so replace the line svg.selectAll("path").attr("d", pathGenerator) with svg.selectAll(".graticule").attr("d", pathGenerator) and svg.selectAll(".country").attr("d", pathGenerator).

    2. When you append the countries you use selectAll('path') like this g.selectAll('path').data(countries.features) ... I think this confuses d3 because you have already appended some path elements so I changed it to a unique selector like this g.selectAll('.country').data(countries.features).

    I'm not 100% sure why d3 behaves like that (maybe @AndrewReid can shed some light) but i've learned from experience that it's best practice to use unique selectors when appending and updating SVG elements with d3.