简体   繁体   中英

hide circles on Orthographic drag

I've created a globe which has circles and a drag. The problem is that the circles appear on the far side of the globe. I would like those circles to be hidden.

My bl.ock can be found here: http://bl.ocks.org/anonymous/dc2d4fc810550586d40d4b1ce9088422/40c6e199a5be4e152c0bd94a13ea94eba41f004b

For example, I would like my globe to function like this one: https://bl.ocks.org/larsvers/f8efeabf480244d59001310f70815b4e

I've seen solutions such as this one: How to move points in an orthogonal map? but it doesn't quite work for me. The points simply disappear, as d[0] and d[1] seem to be undefined.

I've also tried using methods such as this: http://blockbuilder.org/tlfrd/df1f1f705c7940a6a7c0dca47041fec8 but that also doesn't seem to work. The problem here seems to be that he is using the json as his data, while my circles data are independent of the json.

Only similar example I've found is the one: https://bl.ocks.org/curran/115407b42ef85b0758595d05c825b346 from Curran but I don't really understand his code. His method is quite different than mine.

Here is my JavaScript code:

(function(){
 var h = 600;
   var w = 900;
   var i = 0;
   var map = void 0;
  var world = void 0;
  var US = void 0;


    var margin = {
          top: 10,
          bottom: 40,
          left: 0,
          right: 30
        };


      var circleScale = d3.scaleSqrt()
        .domain([0, 4445])
        .range([0.5, 10])


    var width = w - margin.left - margin.right;
    var height = h - margin.top - margin.bottom;

  var dragging = function(d){
    var c = projection.rotate();
    projection.rotate([c[0] + d3.event.dx/6, c[1] - d3.event.dy/6])
    map.selectAll('path').attr('d', path);
    map.selectAll(".circles").attr("cx", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[0];
              })
              .attr("cy", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[1];
              })
   }

   var drag = d3.drag()
    .on("drag", dragging)


   var projection = d3.geoOrthographic().clipAngle(90); 
   var path = d3.geoPath().projection(projection);

   var svg = d3.select("body")
        .append("svg")
        .attr("id", "chart")
        .attr("width", w)
        .attr("height", h)

   d3.json("world.json", function(json){ 
     d3.csv("arms_transfer_2012_2016_top - arms_transfer_2012_2016_top.csv", function(error, data){


    var countries = topojson.feature(json, json.objects.countries).features
    var US = countries[168]


    map = svg.append('g').attr('class', 'boundary');
    world = map.selectAll('path').data(countries);
    US = map.selectAll('.US').data([US]);
    Circles = map.selectAll(".circles").data(data)

    console.log(countries[168])


    world.enter()
    .append("path")
    .attr("class", "boundary")
    .attr("d", path)


    US.enter()
      .append("path")
      .attr("class", "US")
      .attr("d", path)
      .style("fill", "lightyellow")
      .style("stroke", "orange")




          Circles.enter()
              .append("circle")
              .attr("class", "circles")
              .attr("r", function(d){
                return circleScale(d.Millions)
              })
              .attr("cx", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[0];
              })
              .attr("cy", function(d){
                var coords = projection([d.Longitude_imp, d.Latitude_imp])
                return coords[1];
              })
              .style("fill", "#cd0d0e")

    svg.append("rect")
      .attr("class", "overlay")
      .attr("width", w)
      .attr("height", h)
      .call(drag)

      })

   })

  })();

There are a few different methods to achieve this, but one of the easier methods would be to calculate the angular distance between the projection centroid (as determined by the rotation) and the circle center on the drag event:

map.selectAll("circle")
  .style("display", function(d) {

  var circle = [d.Longitude_imp, d.Latitude_imp];
  var rotate = projection.rotate(); // antipode of actual rotational center.

  var center = [-rotate[0], -rotate[1]]

  var distance = d3.geoDistance(circle,center);
    return (distance > Math.PI/2 ) ? 'none' : 'inline';
})

Take the center of each point and get the rotational center with projection.rotate() - note that the rotation values are inverse of the centering point. A rotation of [10,-20] centers the map at [-10,20], you move the map under you. With these two points we can use d3.geoDistance() which calculates the distance between two points in radians, hence the use of Math.PI/2 - which gives us points outside of 90 degrees, for these we hide, for the rest we show.

This can be incorporated a little nicer into your code, but I keep it separate here to show what is happening clearer.

Here's an example block - drag to trigger, I haven't applied the logic to the initial load.


An alternative approach, as noted by Gerardo Furtado , would be to use a path to display the circles - using path.pointRadius to set the size of the circle for each point. Instead of appending a circle, you could append path with the following format:

Circles.enter()
    .append("path")
          .attr("class", "circles")
          .attr("d",createGeojsonPoint)

The, on update/drag:

map.selectAll('.circles').attr('d',createGeojsonPoint);

This method uses the clip angle of the orthographic to hide features when they are more than 90 degrees from the center of the projection (as determined by rotation). Your createGeojsonPoint function needs to set the radius and return a valid geojson object:

var createGeojsonPoint = function(d) {
    console.log(d);
    path.pointRadius(circleScale(d.Millions));                                      // set point radius
    return path({"type":"Point","coordinates":[d.Longitude_imp,d.Latitude_imp]})    // create geojson point, return path data
}

All together, with the necessary modifications, your code might look like this .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM