DataMaps / скрыть дуги по идентификатору - PullRequest
3 голосов
/ 29 марта 2019

Я создал карту мира, используя DataMaps .

Моя цель - асинхронно отображать и скрывать дуги на карте на основе некоторых данных API.


Что я уже пробовал

Я звоню своему API каждые 5 секунд и помещаю данные ответов в карту. (Это будет заменено асинхронными вызовами в будущем)

В моем примере ниже 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 ]

2 голосов
/ 02 апреля 2019

Все дуги, добавленные с помощью Карты данных , на самом деле имеют ключ data в формате JSON ( datamaps.js # L356 ):

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

Обратите внимание, что DataMaps использует JSON.stringify в качестве ключевой функции . Было бы неплохо, если бы DataMaps предоставили здесь возможность использовать пользовательскую функцию клавиш, но, увы ...

Хотя сами эти ключи не сохраняются, достаточно убедиться, что для одних и тех же данных будет только одна дуга. Данные дуги - это сам идентификатор дуги.

Используя эти знания, мы можем определить дугу, сравнив ее данные:

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

Чтобы продвинуться дальше, мы можем настроить данные, которые мы передаем в DataMaps.arc, чтобы они действительно содержали наш дружественный для сравнения идентификатор. Поле origin и destination является обязательным, но мы можем использовать любое другое поле по своему усмотрению.

{
  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 голос
/ 01 апреля 2019

Ну, я попытался решить вашу проблему. Я назначил уникальные идентификаторы каждой дуге карты данных. Это легко даст вам доступ к независимым дугам, и вы сможете соответствующим образом изменить их переходы.

Для демонстрации я случайно отложил его, и он работает правильно. Я задержал первую дугу на 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>

Надеюсь, это решит вашу проблему. Удачного кодирования! И спасибо, что заставили меня исследовать карты данных.

...