2015-07-17 11 views
5

Ich versuche, Spinning Globe mit Bars wie in this example zu erstellen. Sie können mein Beispiel here sehen. Und alles geht gut, bis Bars über den Horizont gehen. Ich habe keine Ahnung, wie man Balken von unten schneidet, wenn sie auf der anderen Seite des Planeten sind. Jeder kann mir vorschlagen, wie es geht?d3.js. Spinning Globe mit Bars

/* 
* Original code source 
* http://codepen.io/teetteet/pen/Dgvfw 
*/ 

var width = 400; 
var height = 400; 
var scrollSpeed = 50; 
var current = 180; 

var longitudeScale = d3.scale.linear() 
    .domain([0, width]) 
    .range([-180, 180]); 

var planetProjection = d3.geo.orthographic() 
    .scale(200) 
    .rotate([longitudeScale(current), 0]) 
    .translate([width/2, height/2]) 
    .clipAngle(90); 
var barProjection = d3.geo.orthographic() 
    .scale(200) 
    .rotate([longitudeScale(current), 0]) 
    .translate([width/2, height/2]) 
    .clipAngle(90); 

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

var svg = d3.select("body").append("svg") 
    .attr("width", width) 
    .attr("height", height); 

d3.json("https://dl.dropboxusercontent.com/s/4hp49mvf7pa2cg2/world-110m.json?dl=1", function(error, world) { 
    if (error) throw error; 

    var planet = svg.append("path") 
    .datum(topojson.feature(world, world.objects.land)) 
    .attr("class", "land") 
    .attr("d", path); 

    d3.csv("https://dl.dropboxusercontent.com/s/v4kn2hrnjlgx1np/data.csv?dl=1", function(error, data) { 
    if (error) throw error; 

    var max = d3.max(data, function(d) { 
     return parseInt(d.Value); 
    }) 

    var lengthScale = d3.scale.linear() 
     .domain([0, max]) 
     .range([200, 250]) 

     var bars = svg.selectAll(".bar") 
     .data(data) 
     .enter() 
     .append("line") 
     .attr("class", "bar") 
     .attr("stroke", "red") 
     .attr("stroke-width", "2"); 

    function bgscroll() { 

     current += 1; 

     planetProjection.rotate([longitudeScale(current), 0]); 
     barProjection.rotate([longitudeScale(current), 0]); 

     planet.attr("d", path); 

     bars.attr("x1", function(d) { 
     return planetProjection([d.Longitude, d.Latitude])[0]; 
     }).attr("y1", function(d) { 
     return planetProjection([d.Longitude, d.Latitude])[1]; 
     }).attr("x2", function(d) { 
     barProjection.scale(lengthScale(d.Value)); 
     return barProjection([d.Longitude, d.Latitude])[0]; 
     }).attr("y2", function(d) { 
     barProjection.scale(lengthScale(d.Value)); 
     return barProjection([d.Longitude, d.Latitude])[1]; 
     }); 
    } 

// bgscroll(); 
    setInterval(bgscroll, scrollSpeed); 
    }) 
}) 

Antwort

3

Um die Stangen abschneiden an Am Horizont fügen wir eine Maske mit der Mitte des Globus 2D Zentrum und mit seinem Radius. Dann wenden wir diese Maske nur an, wenn die untere Kante den Horizont kreuzt (durch Verfolgen der Länge).

Erstellen der Maske

// get the center of the circle 
var center = planetProjection.translate(); 
// edge point 
var edge = planetProjection([-90, 90]) 
// radius 
var r = Math.pow(Math.pow(center[0] - edge[0], 2) + Math.pow(center[1] - edge[1], 2), 0.5); 

svg.append("defs") 
    .append("clipPath") 
    .append("circle") 
    .attr("id", "edgeCircle") 
    .attr("cx", center[0]) 
    .attr("cy", center[1]) 
    .attr("r", r) 

var mask = svg.append("mask").attr("id", "edge") 
mask.append("rect") 
    .attr("x", 0) 
    .attr("y", 0) 
    .attr("width", "100%") 
    .attr("height", "100%") 
    .attr("fill", "white"); 
mask.append("use") 
    .attr("xlink:href", "#edgeCircle") 
    .attr("fill", "black"); 

Anwendung der Maske

.... bars .... 
.attr("mask", function (d) { 
    // make the range from 0 to 360, so that it's easier to compare 
    var longitude = Number(d.Longitude) + 180; 
    // +270 => -90 => the position of the left edge when the center is at 0 
    // -value because a rotation to the right => left edge longitude is reducing 
    // 360 because we want the range from 0 to 360 
    var startLongitude = 360 - ((longitudeScale(current) + 270) % 360); 
    // the right edge is start edge + 180 
    var endLongitude = (startLongitude + 180) % 360; 
    if ((startLongitude < endLongitude && longitude > startLongitude && longitude < endLongitude) || 
     // wrap around 
     (startLongitude > endLongitude && (longitude > startLongitude || longitude < endLongitude))) 
     return null; 
    else 
     return "url(#edge)"; 
}); 

Wir auch dies durch Messen des Abstandes tun könnte.


Fiddle - http://jsfiddle.net/gp3wvm8o/


enter image description here

+0

Das ist es! Vielen Dank. – zeleniy

0

nur den Überblick über den Bereich des sichtbaren Längen halten und die Bars verstecken, wenn sie nicht in diesem Bereich sind

.attr("display", function(d) { 
    // make the range from 0 to 360, so that it's easier to compare 
    var longitude = Number(d.Longitude) + 180; 
    // +270 => -90 => the position of the left edge when the center is at 0 
    // -value because a rotation to the right => left edge longitude is reducing 
    // 360 because we want the range from 0 to 360 
    var startLongitude = 360 - ((longitudeScale(current) + 270) % 360); 
    // the right edge is start edge + 180 
    var endLongitude = (startLongitude + 180) % 360; 
    if ((startLongitude < endLongitude && longitude > startLongitude && longitude < endLongitude) || 
     // wrap around 
     (startLongitude > endLongitude && (longitude > startLongitude || longitude < endLongitude))) 
     return "block"; 
    else 
     return "none"; 
}) 

Fiddle - http://jsfiddle.net/b12ryhda/

+0

Danke. Das ist eine gute Idee, aber ich möchte nicht Bars auf einmal verstecken, aber nach und nach, wie in Beispiel ich oben – zeleniy

+0

@Zeleniy - habe es als eine separate Antwort gepostet, da der Kernteil wesentlich anders ist (Display vs. Maske) – potatopeelings