2015-07-17 39 views
5

Sto provando a creare un globo rotante con barre come in this example. Puoi vedere il mio esempio here. E tutto va bene fino a quando le sbarre non vanno oltre l'orizzonte. Non ho idea di come tagliare le barre dal basso quando sono dall'altra parte del pianeta. Qualcuno può suggerirmi come farlo?d3.js. Globo rotante con barre

/* 
* 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); 
    }) 
}) 

risposta

3

Per clip fuori le sbarre All'orizzonte, aggiungiamo una maschera centrata al centro del globo 2D e con il suo raggio. Quindi applichiamo questa maschera solo se il bordo inferiore attraversa l'orizzonte (rintracciando la longitudine).

la creazione della maschera

// 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"); 

di applicare la maschera

.... 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)"; 
}); 

Potremmo anche farlo misurando la distanza.


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


enter image description here

+0

Questo è tutto! Grazie. – zeleniy

0

Basta tenere traccia della gamma di longitudini visibili e nascondere le barre se non sono in quella fascia di

.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

Grazie. Questa è una buona idea, ma non voglio nascondere le barre contemporaneamente, ma gradualmente, come nell'esempio che fornisco in cima – zeleniy

+0

@zeleniy - l'ho postata come risposta separata poiché la parte centrale è essenzialmente diversa (display vs mask) – potatopeelings