0

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

Gleb
  • 107
  • 1
  • 9
  • Do you have a live demo or a JSFiddle or something? It would be helpful to see what you mean by 'breaks how the map looks'. – Jacob Philpott Jan 18 '21 at 21:17
  • Fair enough, I will try to make one. – Gleb Jan 18 '21 at 21:23
  • Also the map actually looks more smoth on the demo website than my local machine... I think I just need to update all the elements properly when dragging, but I am not sure how to do that. – Gleb Jan 18 '21 at 21:27
  • Okay.. is this the look you are going for? https://vizhub.com/jgphilpott/59c2c92f1ad840268f99be8bbcae00b1?edit=files&file=index.js – Jacob Philpott Jan 18 '21 at 22:13
  • Might also be a problem of SVG. You could also use a canvas renderer, see the great example from Mike Bostock: https://observablehq.com/@d3/versor-dragging – Sven Lauterbach Jan 18 '21 at 22:15
  • I believe I got all the components rotating smoothly together.. if this is the effect you were wanting i'll explain what I did in an answer.. if not let me know what i'm missing and i'll keep working on it. – Jacob Philpott Jan 18 '21 at 22:23
  • yes @JacobPhilpott that looks great! – Gleb Jan 18 '21 at 22:34
  • @Gleb, you'll see perfomance gains as well if you use `110m.json` rather than `50m.json` from the topojson atlas - the level of detail in 50m is in excess of your resolution given the sizing of the globe. – Andrew Reid Jan 18 '21 at 22:35
  • also if you don't mid. In the code demo, I was also trying to get a country name by clicking on it, but the on click listener does not seem to return the name. What am I doing wrong there? Because i can append the text with the country... – Gleb Jan 18 '21 at 22:35
  • @AndrewReid Is it possible to dynamically change them if I zoom in for example? – Gleb Jan 18 '21 at 22:37
  • @Gleb, you could use different files for each zoom depth no problem - there are still optimization tricks you might want to employ to avoid projecting all the data that extends beyond the frame though. That should probably be a separate question - however, I could, sometime later tonight, provide a number of options for optimizing your rotation first though (if others don't beat me to it). – Andrew Reid Jan 18 '21 at 22:53
  • @Gleb I've posted my answer and upvoted your question.. as far as the on click event listener is concerned I MIGHT try and help you out with it tomorrow but i’m going to bed now, it's late here. – Jacob Philpott Jan 18 '21 at 23:34
  • @AndrewReid I would love to hear your advice on how to avoid projecting all the data that extends beyond the frame! In a separate project that I'm working on I have a large full screen map with multiple layers and it has a tendency to get a little laggy. If I post a question about that maybe you could provide your answer? – Jacob Philpott Jan 18 '21 at 23:41
  • Hey @AndrewReid I just posted a fresh question to ask about how you can avoid projecting data that extends beyond the frame.. if you have any tips or tricks that would be awesome! https://stackoverflow.com/q/67109209/1544937 – Jacob Philpott Apr 15 '21 at 13:19
  • 1
    @JacobPhilpott, will take a look this week, have a few suggestions. – Andrew Reid Apr 19 '21 at 18:55
  • @AndrewReid that would be great! I look forward to your suggestions! – Jacob Philpott Apr 22 '21 at 06:08

1 Answers1

1

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.

Jacob Philpott
  • 538
  • 1
  • 8
  • 24