DataMaps /按标识符隐藏弧

时间:2019-03-29 08:39:49

标签: javascript jquery datamaps

我已经使用DataMaps创建了世界地图。

我的目标是根据一些API数据异步显示和隐藏地图上的弧。


我已经尝试过什么

我每5秒打电话给API,并将响应数据推送到地图中。 (将来将由异步调用替换)

在下面的示例中,arcData array代表我的API响应。

我可以通过DOM manipulation访问弧。 就我而言,我正在使用d3.selectAll('path.datamaps-arc').transition().duration(3500).style("opacity", 0);慢慢淡出所有弧,然后将其删除。

var arcData = //Test Data
[
  {
    origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 37.618889,
          longitude: -122.375
    }
 },
 {   origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 25.793333,
          longitude:-80.290556
    }
 },
 {
    origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 35.877778,
          longitude: -78.7875
    }
 }
];


$(document).ready(function() {
  var map = new Datamap({ //create data map
    element: document.getElementById('container'),
    fills: {
      defaultFill: "#343a40",
    }
  });
  
  //call API every 4 seconds [Workaround for this fiddle]  
  setInterval(function() {
    //add arcs to map
    map.arc(arcData, {strokeWidth: 2, animationSpeed: 1000, strokeColor: '#b1dd00'}); // add arc Data

    //Remove all arcs [should be replaced by a function that asynchronously hides single arcs after x seconds]
    d3.selectAll('path.datamaps-arc').transition().duration(3500).style("opacity", 0);
    d3.selectAll('path.datamaps-arc').transition().delay(3500).remove();
  }, 4000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="https://datamaps.github.io/scripts/datamaps.world.min.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>


该解决方案基本上可以使用,但是:

我的问题

同时隐藏所有弧。 如果将来我异步调用该API,则在当前绘制弧时会发生冲突,同时触发删除过程。

我想要的

一种解决方案,我可以通过某些标识符访问每个弧,并在完全绘制后分别将其删除。

2 个答案:

答案 0 :(得分:2)

通过DataMaps添加的所有弧实际上都由其data以JSON格式(datamaps.js#L356)进行键控:

var arcs = layer.selectAll('path.datamaps-arc').data( data, JSON.stringify ); 

请注意,DataMaps使用JSON.stringify作为键功能如果DataMaps提供了一种在此处使用自定义键功能的方法,那就太好了,但是a ...

虽然这些键本身没有保留,但足以确保我们对于一个相同的数据仅存在一个弧。弧数据是弧标识符本身。

利用这些知识,我们可以通过比较弧线的数据来识别弧线:

var selectedArcs = d3.selectAll('path.datamaps-arc').filter(function(data) {
   // compare data
   return data === someValue;
});

进一步讲,我们实际上可以调整传递给DataMaps.arc的数据,以便它实际上包含我们的比较友好标识符。 origindestination字段是必填字段,但我们可以随意使用其他任何字段。

{
  id: 'some-unique-identifier',
  origin: {
    latitude: 52.520008,
    longitude: 13.404954
  },
  destination: {
    latitude: 37.618889,
    longitude: -122.375
  }
}

然后我们可以使用此调整字段来标识我们的弧线:

var selectedArcs = d3.selectAll('path.datamaps-arc').filter(function(data) {
   return data.id === 'some-unique-identifier';
});
  

请记住,DataMaps使用整个data的值来键接我们的每个弧;意味着两个id相同但origin和或destination值不同的数据将被视为两个不同的弧。

这是使用原始示例的修改版本的简单演示:

var arcData = //Test Data
[
  {
    id: 123,
    origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 37.618889,
          longitude: -122.375
    }
 },
 	{ 
  	id: 'abc',
    origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 25.793333,
          longitude:-80.290556
    }
 },
 	{
    id: 'xyz',
    origin: 
    {
          latitude: 52.520008,
          longitude: 13.404954
    },
    destination: {
          latitude: 35.877778,
          longitude: -78.7875
    }
 }
];


$(document).ready(function() {
  var map = new Datamap({ //create data map
    element: document.getElementById('container'),
    fills: {
      defaultFill: "#343a40",
    }
  });
  
  function drawMap() {
    map.arc(arcData, {strokeWidth: 2, animationSpeed: 1000, strokeColor: '#b1dd00'});
  };
  
  function removeArc(id) {  
    var all = d3.selectAll('path.datamaps-arc');
    var sel = all.filter(function(data) {
      return data.id === id;
    });
    sel.transition().duration(1000).style("opacity", 0).remove();
  };
  
  $('button').on('click', function(){ 
    var id = $(this).data('arc');
    if (id) {
      removeArc(id);
    } else {
      drawMap();
    }
  });
  
  drawMap();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="https://datamaps.github.io/scripts/datamaps.world.min.js"></script>

<button type="button" data-arc="123">Remove Arc id:123</button>
<button type="button" data-arc="abc">Remove Arc id:abc</button>
<button type="button" data-arc="xyz">Remove Arc id:xyz</button>
<button type="button">Redraw</button>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>

答案 1 :(得分:1)

好吧,我试图解决您的问题。我所做的是,我为数据映射的每个弧都分配了唯一的ID。它将轻松为您提供访问独立弧的权限,并可以相应地更改其过渡。

出于演示目的,我已将其随机延迟,并且可以正常工作。我将第一弧延迟了1000毫秒,将第二弧延迟了2000毫秒,将第三弧延迟了3000毫秒。您可以根据需要实现自己的算法来延迟弧的过渡。我在代码中添加了注释,供您参考。

由于setInterval每隔4000毫秒运行一次,如果任何电弧的延迟超过4000毫秒,那么您将只能看到一次同时产生一次所有电弧。之后,弧的产生将非常随机,因此请记住这一点。

var arcData = //Test Data
                [{
                        origin: {
                            latitude: 52.520008,
                            longitude: 13.404954
                        },
                        destination: {
                            latitude: 37.618889,
                            longitude: -122.375
                        }
                    },
                    {
                        origin: {
                            latitude: 52.520008,
                            longitude: 13.404954
                        },
                        destination: {
                            latitude: 25.793333,
                            longitude: -80.290556
                        }
                    },
                    {
                        origin: {
                            latitude: 52.520008,
                            longitude: 13.404954
                        },
                        destination: {
                            latitude: 35.877778,
                            longitude: -78.7875
                        }
                    }
                ];


            $(document).ready(function() {
                var map = new Datamap({ //create data map
                    element: document.getElementById('container'),
                    fills: {
                        defaultFill: "#343a40",
                    }
                });
                //hide arc function which will take x amount of delay and arc-id which you want to delay.
                function hideArc(delay, arcId) {
                    d3.select('#' + arcId).transition().duration(delay).style("opacity", 0);
                    d3.select('#' + arcId).transition().delay(delay).remove();
                }

                //call API every 4 seconds [Workaround for this fiddle]  
                setInterval(function() {
                    //add arcs to map
                    map.arc(arcData, {
                        strokeWidth: 2,
                        animationSpeed: 1000,
                        strokeColor: '#b1dd00'
                    }); // add arc Data
                    let arcIds = [];// it will hold all the unique arc-ids
                    d3.selectAll('path.datamaps-arc')[0].forEach((ele, index) => {
                        ele.setAttribute('id', 'datamap-arc-' + index);
                        arcIds.push('datamap-arc-' + index);// pushing new generated ids to arcIds array
                    });
                    //mapping of delay and arc-id, this part is replaceable, you can change it the way you want to change the delay of respective arc.   
                    let arcIdAndDelaymapping = arcIds.map((aercId, index) => {
                        return {
                            aercId,
                            delay:1000*(index+1)
                        }
                    })


                    //Remove all arcs [should be replaced by a function that asynchronously hides single arcs after x seconds]
                    //calling hideArc function with their respective delays.  
                    arcIdAndDelaymapping.forEach((arcMapping) => {
                        hideArc(arcMapping.delay, arcMapping.aercId);
                    })
                }, 4000);
            });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"></script>
<script src="https://datamaps.github.io/scripts/datamaps.world.min.js"></script>
<div id="container" style="position: relative; width: 500px; height: 300px;"></div>

希望它将解决您的问题。快乐编码!感谢您让我探索数据地图。