2015-07-17 4 views
5

Я пытаюсь создать вращающийся шар с барами, как в this example. Вы можете увидеть мой пример here. И все идет хорошо, пока бары не перейдут за горизонт. Я понятия не имею, как вырезать решетки со дна, когда они находятся на другой стороне планеты. Кто-нибудь может предложить мне, как это сделать?d3.js. Спиннинговый глобус с барами

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

ответ

3

Чтобы обрезать бруски на горизонт, мы добавляем маску, центрированную в 2D-центре земного шара и с его радиусом. Затем мы применяем эту маску только в том случае, если нижний край пересекает горизонт (путем отслеживания долготы).

Создание маски

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

Применение маски

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

Мы также могли бы сделать это путем измерения расстояния.


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


enter image description here

+0

Это все! Спасибо. – zeleniy

0

Просто отслеживать диапазон видимых долгот и скрыть полосы, если они не находятся в этом диапазоне

.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

Спасибо. Это хорошая идея, но я не хочу сразу скрывать бары, но постепенно, как и в примере, я предоставляю сверху – zeleniy

+0

@zeleniy - отправил его как отдельный ответ, поскольку основная часть существенно отличается (отображение против маски) – potatopeelings

 Смежные вопросы

  • Нет связанных вопросов^_^