mercredi 28 janvier 2015

How to position svg group or foreignobject correctly onto d3 globe?


I am quite new to d3 and am fighting with one issue for two weeks now.


My goal is to render multiple svg:foreignobjects (which has xhtml:divs) onto a d3 globe. The content/data for the xhtml:div comes from a csv. The initial setup works so far, so when the page initially loads, the divs (currently city names) are located on their corresponding correct positions.


enter image description here


But when rotating the globe with the mouse, the position of the xhtml:foreignobjects (or xhtml:divs) are messed up: enter image description here


Can anybody help here or can give a hint into the right direction?


Live Demo


Code:



<script>
var width = 960, height = 500, rotate = [ 0, 0 ], graticule = d3.geo.graticule();

var projection = d3.geo.orthographic().scale(width / (2 * Math.PI)).clipAngle(90);

var mercator = d3.geo.mercator().scale(width / (2 * Math.PI));

var path = d3.geo.path().projection(projection);

var m0, o0;

var cx, cy = {};
var svg;

var cities_csv;

var drag = d3.behavior.drag().on("dragstart", function() {
d3.event.sourceEvent.stopPropagation();

// Adapted from http://ift.tt/1BBRLu6 and updated for d3 v3
var proj = projection.rotate();
m0 = [ d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY ];
o0 = [ -proj[0], -proj[1] ];
}).on(
"drag",
function() {

if (m0) {
var m1 = [ d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY ], o1 = [
o0[0] + (m0[0] - m1[0]) / 4, o0[1] + (m1[1] - m0[1]) / 4 ];
projection.rotate([ -o1[0], -o1[1] ]);
}

// Update the map
path = d3.geo.path().projection(projection);
d3.selectAll("path").attr("d", path);

var group = svg.selectAll("g");

//console.log(d3);

d3.selectAll(".point").each(function(d, i) {


console.log(d);

d3.select("#f" + i).attr("x",
projection([ d.coordinates[0], d.coordinates[1] ])[1]+200);
d3.select("#f" + i).attr("y",
projection([ d.coordinates[0], d.coordinates[1] ])[0]-200);

console.log("NR:" + i);


});

});

d3.select("svg").on("mousedown", function() {
console.log("mouse: " + projection.invert(d3.mouse(this)));
});

svg = d3.select("#map").append("svg").attr("width", width).attr("height", height).call(drag).call(
d3.behavior.zoom().on("zoom", redraw));

function redraw() {
//Yet commented out because I just want to scale, not translate
//svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
};

svg.append("defs").append("path").datum({
type : "Sphere"
}).attr("id", "sphere").attr("d", path);

svg.append("use").attr("class", "stroke").attr("xlink:href", "#sphere");

svg.append("use").attr("class", "fill").attr("xlink:href", "#sphere");

svg.append("path").datum(graticule).attr("class", "graticule").attr("d", path);
svg.style("shape-rendering", null);

d3.json("world-110m2.json", function(error, data) {
svg.insert("path", ".graticule").datum(topojson.feature(data, data.objects.countries))
.attr("class", "land").attr("d", path);
});

d3.csv("cities.csv", function(error, data) {
var cities_csv = data;
data.forEach(function(d, i) {

var group = d3.select("svg").append("svg:g").attr("class", "group").attr("id", "g" + i);

var point = group.append("path", ".foreground").datum({
type : "Point",
coordinates : [ d['lon'], d['lat'] ]
}).attr("class", "point").attr("id", "p" + i).attr("data-id", i).attr("d", path).on("click",
function() {
window.open("http://google.com");
}).attr("r", 4).style("fill", "red");

point.select("div").html('<a href= "http://google.com">' + // The first <a> tag
(d.date) + "</a>" + // closing </a> tag
"<br/>" + d.close);


//check if location is clipped
var clipped = false;
clip_test_path = d3.geo.path().projection(projection);
if (typeof (clip_test_path({
type : "MultiPoint",
coordinates : [ [ d.lon, d.lat ] ]
})) === "undefined") {
clipped = true
}

if (clipped == false) {
group.append("foreignObject").attr("d", path).attr("id", "f" + i).attr("data-id", i).attr('class',
'city').attr('width', '100px').attr('height', '100px').attr("x",
projection([ d.lon, d.lat ])[0]).attr("y", projection([ d.lon, d.lat ])[1]).append(
'xhtml:div').style("width", "20px").style("height", "20px").style("padding", "2px").html(
"<small>" + d.city + "</small>");

}

});

});
</script>




Aucun commentaire:

Enregistrer un commentaire