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.