全球的远方

时间:2017-12-11 00:10:34

标签: javascript d3.js svg

所以,我有旋转的Globe,有些国家强调,如果你鼠标悬停它们。 但问题是,远方国家不应该改变颜色,土地应该改变边界。 我想,原因是clipAngle,并尝试将其更改为不同的地图"那样:

projection.clipAngle(180);
// Draw the world 
var world = 
svg.selectAll('.lands').data([land]).enter().append('path').attr("class", "lands").attr('d', path);
// Draw the cities
var cities = svg.selectAll('.cities').data([points]).enter().append('path').attr("class","cities").attr('d', path);
projection.clipAngle(90);
//Draw the countries
d3.json("https://rawgit.com/Bramsiss/Globe/master/world-counries1.json", function(collection) { 
var countries = svg.selectAll(".country").data(collection.features).enter().append("path").attr("d", path).attr("class", "country");

但它没有用。 接下来我想做两个投影whit clipAngle(对于相同地图的不同颜色),但这是个坏主意。

也许这太简单了,但我试着解决这个问题已经三天了,还没有得到答案......

此外,请在codepen(https://codepen.io/bramsis/pen/ZvzGdo

中使用此功能

2 个答案:

答案 0 :(得分:1)

如果国家/地区“落后于”全球,您只需修改适用于您所在国家/地区的mouseover功能。

 countries
        .on("mouseover", function(d) {
          // we need a coordinate to work with
          // this is just a quick and dirty method - see 
          // the answer for why this part is ambiguous... 
          var data =
            d.geometry.coordinates[0][0].length == 2
              ? d.geometry.coordinates[0][0]
              : d.geometry.coordinates[0][0][0];
          // next using the coordinate we work out the angular distance 
          var ad = d3.geoDistance(data, [
            -projection.rotate()[0],
            projection.rotate()[1]
          ]);
          // if the angle is less than Pi/2
          // then the coordinate is in front of the horizon and the country 
          // is highlighted 
          if (ad < Math.PI/2) {
            this.style.stroke = "rgba(193, 0, 32, 0.5)";
          }
        })

显然这有点粗糙并准备就绪,因为它只选择代表国家的第一个坐标......你需要考虑你所说的“远方” - 即整个国家,它的任何部分, 50%等等?一旦你定义了“远端”的意思,那么你可以使用这个方法迭代代表你感兴趣的部分的坐标集,看看它们是否在地平线前面。

查看working fork of your example

要了解我的意思,只使用一个坐标,鼠标悬停在南极上进行清晰演示 - 50%的时间会突出显示 - 尽管它的某些部分始终在视图中,计算所依据的确切坐标只有50%的时间才能看到。

编辑:

来自评论......

  

谢谢,它确实有效,但如果地球仪不仅可以旋转,那该怎么办?   X坐标,但也是Y.

要围绕y轴和x轴旋转,只需要修改距离的计算。即。

      // notice the negative phi value 
      // for reference the values are; lambda, phi, gamma
      var ad = d3.geoDistance(data, [
        -projection.rotate()[0],
        -projection.rotate()[1],
        0
      ]);

显然要看到这个工作,你需要实际沿Y轴旋转地球。 e.g。

// rotate the globe over the x and y axes
// i just use slightly different values here to "tumble" the globe
projection.rotate([rotate[0] + velocity[0] * dt, velocity[0] * dt]);

查看另一个working fork with rotation in multiple directions

也可以在projection rotate上阅读,因为它确实是你在这里想要实现的目标的关键。

答案 1 :(得分:1)

一种可能的解决方案是使用main :null run :what? 将您的contries原始路径与另一层相同路径叠加。只有这些路径才会为附加到它们的.clipAngle(90)mouseover事件获取事件处理程序。这样,只有面向前方的国家才会对这些鼠标事件作出反应。

以下代码段演示了一种可行的解决方案。它没有多大的魔力,因为你只需复制现有的逻辑来添加国家。为清晰起见,我使用mouseout为部分添加了前缀。

frontXxx

请注意,根据comment对弗雷泽的答案提出的要求,如何轻松地绕多个轴旋转。

var frontProjection = d3.geoOrthographic()
  .scale(radius)
  .translate(translation)
  .clipAngle(90);

var frontPath = d3.geoPath()
  .projection(frontProjection)
  .pointRadius(1);

// ...

// Inside the load() function.
var frontCountries = svg.selectAll(".frontCountry")
  .data(collection.features)
  .enter().append("path")
    .attr("d", frontPath)
    .attr("class", "frontCountry")
    .on("mouseover", function() {
      this.style.stroke = 'rgba(193, 0, 32, 0.5)';
    })
   .on("mouseout", function() {
      this.style.stroke = 'rgba(0, 0, 0, 0)';
   });

// ...

// Rotation
var frontFeature = svg.selectAll(".frontCountry");
timer = d3.timer(function() {
  var dt = Date.now() - time;
  // ...
  frontProjection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
  frontFeature.attr("d", frontPath);     
});
/* Set up */
/* ====== */
var timer;
var width = 1000,
height = 700,
radius = 325,
originalScale = height / 2,
scale = originalScale,
translation = [width / 2, height / 2],
scaleChange, rotation,
rotate = [100, -1],
velocity = [.009,.005],
time = Date.now();
var graticule = d3.geoGraticule();

// set up the main canvas and the projection
var svg = d3.select('#map').append('svg').attr("id", "world").attr('width',width).attr('height',height).append('g');
var projection = d3.geoOrthographic().scale(radius).translate(translation).clipAngle(180);
var frontProjection = d3.geoOrthographic().scale(radius).translate(translation).clipAngle(90);
var path = d3.geoPath().projection(projection).pointRadius(1);
var frontPath = d3.geoPath().projection(frontProjection).pointRadius(1);
svg.append("circle").attr("cx", width/2).attr("cy", height/2).attr("r", radius).classed('cir', true);

/* Data load */
/* ========= */
d3.queue()
.defer(d3.csv, 'https://rawgit.com/Bramsiss/Globe/master/c.csv')
.defer(d3.json, 'https://rawgit.com/jonataswalker/map-utils/master/data/json/world-110m.json')
.await(load);

function load(error, cities, world ) {
  if (error) { console.log(error); }

  var land = topojson.feature(world, world.objects.land),
      grid = graticule();

  var outerArray = [];
  cities.forEach(function(el) {

    var innerArray = [+el.Longitude, +el.Latitude];
    outerArray.push(innerArray);
  }); 

  var points = { 
    type: "MultiPoint", 
    coordinates: outerArray 
  }; 
  
  // Draw the world 
  var world = svg.selectAll('.lands').data([land]).enter().append('path').attr("class", "lands").attr('d', path);

  // Draw the cities
  var cities = svg.selectAll('.cities').data([points]).enter().append('path').attr("class","cities").attr('d', path);


  //Countries
  d3.json("https://rawgit.com/Bramsiss/Globe/master/world-counries1.json", function(collection) {	
  var countries = svg.selectAll(".country").data(collection.features).enter().append("path").attr("d", path).attr("class", "country");
  
    var frontCountries = svg.selectAll(".frontCountry")
      .data(collection.features)
      .enter().append("path")
        .attr("d", frontPath)
        .attr("class", "frontCountry")
        .on("mouseover", function() {
          this.style.stroke = 'rgba(193, 0, 32, 0.5)';
        })
       .on("mouseout", function() {
          this.style.stroke = 'rgba(0, 0, 0, 0)';
       });

 
  //Rotation
   var feature = svg.selectAll(".cities, .country, .lands");
   var frontFeature = svg.selectAll(".frontCountry");
   timer = d3.timer(function() {
   var dt = Date.now() - time;
   projection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
   feature.attr("d", path);
   frontProjection.rotate([rotate[0] + velocity[0] * dt, rotate[1] + velocity[1] * dt]);
   frontFeature.attr("d", frontPath);     
      
   function rot(dt) {
     return 
   }
  });
});
}
.country {
        fill: transparent;  
        stroke: rgba(0, 0, 0, 0); 
        stroke-width: 2px;
      }      
      .frontCountry {
        fill: darkolivegreen;  
        fill-opacity: 0.6;
        stroke: rgba(0, 0, 0, 0); 
        stroke-width: 2px;
      }      
       .lands {
        fill: transparent;  
        stroke: rgba(0, 0, 0, 1);
        stroke-width: 1px;
      }      
      .cities {
        stroke: #2F4F4F;
        stroke-width: 12px;
        fill:  black;
        opacity:  0.8;
      }
      .cir {
    fill:none;
    stroke:black;
      }

我还forked你想要加入这些变化。