Reputation: 116
Currently I have semi-functional orthographic globe made with D3. It is a map with markers. All "land" zones are well displayed can be rotated by draggin , but svg markers doesn't. Here is my current test code:
var width = 960,
height = 500,
sens = 0.25;
var proj = d3.geo.orthographic()
.scale(220)
.translate([width / 2, height / 2])
.clipAngle(90);
var path = d3.geo.path().projection(proj).pointRadius(function(d) { return 6 });
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height);
var g = svg.append('g');
queue()
.defer(d3.json, 'data/world-110m.json')
.defer(d3.json, 'data/generated.json')
.await(ready);
function ready(error, world, locations) {
g.append('path')
.datum({type: 'Sphere'})
.attr('class', 'water')
.attr('d', path);
g.selectAll('g.land')
.data(topojson.feature(world, world.objects.countries).features)
.enter().append('path')
.attr('class', 'land')
.attr('d', path)
.call(d3.behavior.drag()
.origin(function() {
var r = proj.rotate();
return {x: r[0] / sens, y: -r[1] / sens}; })
.on('drag', function(d) {
var rotate = proj.rotate();
proj.rotate([d3.event.x * sens, -d3.event.y * sens, rotate[2]]);
g.selectAll('.land').attr('d', path);
}));
var nodes = g.selectAll('g.node')
.data(locations)
.enter().append('g').attr('class', 'node')
.attr('transform', function(d) {
var proj_pos = proj([d.lon, d.lat]);
return 'translate(' + proj_pos[0] + ',' + proj_pos[1] + ')';
})
.attr('style', 'cursor: pointer')
.attr("d", path);
nodes.append("svg:image")
.attr('transform', 'translate(-24, -20)')
.attr('width', 20)
.attr('height', 24)
.classed('white',true)
.attr("href","data/marker.svg");
console.log(g.selectAll('g.node'));
}
After trying to make it work some way, I only have been able to clip "points" on the globe, but no SVGs. It seems that svg must go into 'g' tag and results my 'nodes' has no path in 'd' tag.
What I need: whole map must rotate with drag action, with SVGs clipped to location, specified in 'generated.json'
[
{
"lat": 62.782176,
"lon": 54.3214
},
{
"lat": -61.007975,
"lon": -5.05281
},
{
"lat": -78.166262,
"lon": 45.320536
}
]
It is kinda frustrating and if someone could help me, I would be very grateful.
Upvotes: 1
Views: 1031
Reputation: 108557
Updating the position of your nodes is pretty simple. The trick here, though, is to determine when the marker has been rotated "off" the globe (around the back of the map). The only way I can figure to do that is to generate a point path to see if it's undefined:
.on('drag', function(d) {
//update land like you've been doing
var rotate = proj.rotate();
proj.rotate([d3.event.x * sens, -d3.event.y * sens, rotate[2]]);
g.selectAll('.land').attr('d', path);
// for each node
nodes.each(function(d, i) {
var self = d3.select(this),
lon_lat = [d.lon, d.lat];
proj_pos = proj(lon_lat);
// check to see if it's still visible
var hasPath = path({
type: "Point",
coordinates: lon_lat
}) != undefined;
// if it is show it and update position
if (hasPath) {
self.style("display","inline");
self.attr("transform", 'translate(' + proj_pos[0] + ',' + proj_pos[1] + ')');
}
// otherwise hide it
else {
self.style("display","none")
}
});
}));
Full running code.
Upvotes: 1