地球的远端

3

所以,我有一个旋转的地球仪,上面有一些国家被突出显示,如果你将鼠标悬停在它们上面。 但问题是,远离地球的国家不应该改变它们的颜色,而陆地应该改变它们的边框。 我认为原因是剪切角度,尝试将其更改为不同的“地图”,如下所示:

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个回答

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%等。一旦你定义了“远侧”的含义,就可以使用这种方法来迭代表示您感兴趣的部分的坐标集,以查看它们是否在地平线前面。

请参见您示例的工作分支,以了解我所说的只使用一个坐标的含义。

当鼠标悬停在南极上时,可以清楚地演示-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轴旋转地球仪。例如:
// 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]);

请查看另一个可以在多个方向上旋转的工作分支

同时,也可以阅读投影旋转,因为这真正是你在这里尝试实现的关键。


谢谢,它确实有效,但如果地球仅限于X坐标旋转,而不是Y坐标,那该怎么办呢?这样就无法正确工作了,也许可以用Math.abs()解决? - Artemiy
如果您有其他问题,您需要提出,并提供一个您想要尝试的示例,并解释为什么它不起作用。话虽如此,这应该相当简单且类似。 - Fraser

1

可能的解决方案之一是使用.clipAngle(90)在您国家原始路径上叠加相同路径的另一层。只有这些路径才会被添加mouseovermouseout事件处理程序。这样,只有正面的国家会对这些鼠标事件做出反应。

下面的代码片段演示了一个可行的解决方案。没有什么神奇的地方,因为您只需要复制添加国家的现有逻辑。我为部分内容加上了前缀frontXxx以便更清晰明了。

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;
      }
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

<div id="map" text-align="center" style="width:100%; height:100%;">
  <div id="globe"></div>
</div>

我也复制了你的笔记以纳入更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接