Home > Software design >  Points on D3.JS globe not rotating with globe
Points on D3.JS globe not rotating with globe

Time:03-31

I am working with a latitute/longitude coordinate dataset. I have created a globe in d3 to create marker points for each long/lat coord. I have succesfully been able to rotate the globe but the marker points do not rotate and I am not sure how to append them to the rotation?

Thank you

Here is my code below:

<!DOCTYPE html>
<html>
    <body>
        <svg></svg>
        <script src="https://d3js.org/d3.v4.min.js"></script>
        <script src="https://d3js.org/topojson.v1.min.js"></script>
        <script>
            var radius = 250;
            let locations = [];
          
            var projection = d3.geoOrthographic().scale(radius) .translate([radius, radius]).clipAngle(90);  
            var svg = d3.select("body").append("svg").attr("width", radius * 2).attr("height", radius * 2).on("mousedown", mousedown).on("mousemove", mousemove).on("mouseup", mouseup);     
            svg.append("circle") .attr("cx", radius).attr("cy", radius).attr("r", radius).style("fill", "none").style("stroke", "black");     
            var path = d3.geoPath().projection(projection);
            const markerGroup = svg.append('g');
            const center = [radius,radius];

         
            d3.queue()
                .defer(d3.json, 'https://gist.githubusercontent.com/mbostock/4090846/raw/d534aba169207548a8a3d670c9c2cc719ff05c47/world-110m.json')          
                .defer(d3.json, 'data/disdata.json')
                .await((error, worldData, locationData) => {
                    svg.selectAll(".segment")
                        .data(topojson.feature(worldData, worldData.objects.countries).features)
                        .enter().append("path")
                        .attr("class", "segment")
                        .attr("d", path)
                        .style("stroke", "#888")
                        .style("stroke-width", "1px")
                        .style("fill", (d, i) => '#e5e5e5')
                        .style("opacity", ".6")
                        locations = locationData;
                        drawMarkers();                   
                });
            

            function trackballAngles(pt) {
                var r = projection.scale();
                var c = projection.translate();
                var x = pt[0] - c[0], y = - (pt[1] - c[1]), ss = x*x   y*y;


                var z = r*r > 2 * ss ? Math.sqrt(r*r - ss) : r*r / 2 / Math.sqrt(ss);  

                var lambda = Math.atan2(x, z) * 180 / Math.PI; 
                var phi = Math.atan2(y, z) * 180 / Math.PI
                return [lambda, phi];
            }



            function composedRotation(λ, ϕ, γ, δλ, δϕ) {
                λ = Math.PI / 180 * λ;
                ϕ = Math.PI / 180 * ϕ;
                γ = Math.PI / 180 * γ;
                δλ = Math.PI / 180 * δλ;
                δϕ = Math.PI / 180 * δϕ;
                    
                var sλ = Math.sin(λ), sϕ = Math.sin(ϕ), sγ = Math.sin(γ), 
                    sδλ = Math.sin(δλ), sδϕ = Math.sin(δϕ),
                    cλ = Math.cos(λ), cϕ = Math.cos(ϕ), cγ = Math.cos(γ), 
                    cδλ = Math.cos(δλ), cδϕ = Math.cos(δϕ);

                var m00 = -sδλ * sλ * cϕ   (sγ * sλ * sϕ   cγ * cλ) * cδλ,
                        m01 = -sγ * cδλ * cϕ - sδλ * sϕ,
                            m02 = sδλ * cλ * cϕ - (sγ * sϕ * cλ - sλ * cγ) * cδλ,
                    m10 = - sδϕ * sλ * cδλ * cϕ - (sγ * sλ * sϕ   cγ * cλ) * sδλ * sδϕ - (sλ * sϕ * cγ - sγ * cλ) * cδϕ,
                        m11 = sδλ * sδϕ * sγ * cϕ - sδϕ * sϕ * cδλ   cδϕ * cγ * cϕ,
                                m12 = sδϕ * cδλ * cλ * cϕ   (sγ * sϕ * cλ - sλ * cγ) * sδλ * sδϕ   (sϕ * cγ * cλ   sγ * sλ) * cδϕ,
                    m20 = - sλ * cδλ * cδϕ * cϕ - (sγ * sλ * sϕ   cγ * cλ) * sδλ * cδϕ   (sλ * sϕ * cγ - sγ * cλ) * sδϕ,
                        m21 = sδλ * sγ * cδϕ * cϕ - sδϕ * cγ * cϕ - sϕ * cδλ * cδϕ,
                            m22 = cδλ * cδϕ * cλ * cϕ   (sγ * sϕ * cλ - sλ * cγ) * sδλ * cδϕ - (sϕ * cγ * cλ   sγ * sλ) * sδϕ;
                 
                if (m01 != 0 || m11 != 0) {
                    γ_ = Math.atan2(-m01, m11);
                    ϕ_ = Math.atan2(-m21, Math.sin(γ_) == 0 ? m11 / Math.cos(γ_) : - m01 / Math.sin(γ_));
                    λ_ = Math.atan2(-m20, m22);
                } else {
                    γ_ = Math.atan2(m10, m00) - m21 * λ;
                    ϕ_ = - m21 * Math.PI / 2;
                    λ_ = λ;       
                }
                    
                return([λ_ * 180 / Math.PI, ϕ_ * 180 / Math.PI, γ_ * 180 / Math.PI]);
            }
    
            var m0 = null,
            o0;
  
            function mousedown() {  // remember where the mouse was pressed, in canvas coords
                m0 = trackballAngles(d3.mouse(svg.node()));
                o0 = projection.rotate();
                d3.event.preventDefault();
            }

            function mousemove() {
            if (m0) {  // if mousedown
                var m1 = trackballAngles(d3.mouse(svg.node()));
            
                o1 = composedRotation(o0[0], o0[1], o0[2], m1[0] - m0[0], m1[1] - m0[1])
                

                projection.rotate(o1);
                


                svg.selectAll("path").attr("d", path);
                }
            }

            function mouseup() {
            if (m0) {
                mousemove();
                m0 = null;
            }
            }              
            
            function drawMarkers() {
                const markers = markerGroup.selectAll('circle')
                    .data(locations);
                markers
                    .enter()
                    .append('circle')
                    .merge(markers)
                    .attr('cx', d => projection([d.Longitude, d.Latitude])[0])
                    .attr('cy', d => projection([d.Longitude, d.Latitude])[1])
                    .attr('fill', d => {
                        const coordinate = [d.Longitude, d.Latitude];
                        gdistance = d3.geoDistance(coordinate, projection.invert(center));
                        return gdistance > 1.57 ? 'none' : 'steelblue';
                    })
                    .attr('r', 7);

                markerGroup.each(function () {
                    this.parentNode.appendChild(this);
                });
            }
        </script>
    </body>
</html>

CodePudding user response:

On mouse move you update your path:

svg.selectAll("path").attr("d", path);

But you don't update your circles, which you can do by recalculating cx and cy:

  markerGroup.selectAll('circle')
    .attr('cx', d => projection([d.Longitude, d.Latitude])[0])
    .attr('cy', d => projection([d.Longitude, d.Latitude])[1])

So the path moves with the mouse, but the circles stay put.

  • Related