Схема размещения D3 с позиционированием CSS

Я помещаю круги на карту, соответствующую GPS-координатам. Каждый круг содержится в контейнере svg, который помещается на страницу с использованием свойств верхнего и левого CSS. В моей реализации эти контейнеры часто сидят поверх друг друга.

Я пытаюсь реализовать обнаружение столкновений и/или добавить небольшой отрицательный заряд в эти контейнеры, чтобы перекрытия заставляли контейнеры дистанцироваться друг от друга.

До сих пор мои тесты с силовыми макетами либо не приводили к изменению, либо приводили к ошибке ("не могу установить индекс свойства null" или "не может установить свойство x из нуля"). Очевидно, что я делаю что-то неправильно, но я не смог определить путь к разрешению статей, которые я читал в Интернете.

Любые идеи о том, как я могу помешать контейнерам сидеть на одном месте?

var self = this;
var data = [{lat: 127, lon: 36, name: 'a', radius: 9},{lat:127, lon: 36, name: 'b', radius: 9}];

// Position SVG containers correctly
var latLngToPx = function(d) {
 var temp = new google.maps.LatLng(d.lat, d.lon);
 temp = self.map.projection.fromLatLngToDivPixel(temp);
 d.x = temp.x;
 d.y = temp.y;
 return d3.select(this)
 .style('left', d.x + 'px')
 .style('top', d.y + 'px');
};

var collide = function(node) {
 var r = node.radius + 16,
 nx1 = node.x - r,
 nx2 = node.x + r,
 ny1 = node.y - r,
 ny2 = node.y + r;
 return function(quad, x1, y1, x2, y2) {
 if (quad.point && (quad.point !== node)) {
 var x = node.x - quad.point.x,
 y = node.y - quad.point.y,
 l = Math.sqrt(x * x + y * y),
 r = node.radius + quad.point.radius;
 if (l < r) {
 l = (l - r) / l * 0.5;
 node.x -= x *= l;
 node.y -= y *= l;
 quad.point.x += x;
 quad.point.y += y;
 }
 }
 return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
 };
} 

var svgBind = d3.select(settings[type].layer).selectAll('svg')
 .data(data, function(d){ return d.name; })
 .each(latLngToPx);

var svg = svgBind.enter().append('svg')
 .each(latLngToPx)

// svg[0] contains the svg elements
var nodes = svg[0];
var force = d3.layout.force()
 .nodes(nodes)
 .charge(-100)
 .start();

force.on('tick', function(){
 var q = d3.geom.quadtree(nodes),
 i = 0,
 n = nodes.length;

 while (++i < n) {
 q.visit(collide(nodes[i]));
 }

 svg
 .style('left', function(d){ return (d.x - lm.config.offset) + 'px';})
 .style('top', function(d){ return (d.y - lm.config.offset) + 'px';});
});

var circ = svg.append('circle')
 .attr('r', settings[type].r)
 .attr('cx',10)
 .attr('cy',10)
1 ответ

Вам не нужно будет самостоятельно обнаруживать столкновение - макет силы должен позаботиться об этом для вас. Вот основные шаги, которые вам нужно предпринять.

  • Каждому элементу данных, представляющему круг, добавьте элементы x и y которые содержат их текущие (экранные) координаты. Это то, на что будет работать силовая макета.
  • Передайте массив этих элементов в макет силы как узлы. Нет необходимости устанавливать ссылки для начала, хотя вы можете захотеть сделать это позже, чтобы управлять размещением узлов относительно друг друга.
  • Запустите макет силы.
  • Для каждого тика перерисуйте элементы в соответствующем положении.
  • Измените параметры макета силы по своему вкусу.

Вы делаете большую часть этого уже, я просто упоминаю это снова, чтобы уточнить. Код будет выглядеть примерно так.

function latLngToPx(d) {
 var temp = new google.maps.LatLng(d.lat, d.lon);
 temp = self.map.projection.fromLatLngToDivPixel(temp);
 d.x = temp.x;
 d.y = temp.y;
};
data.forEach(function(d) { latLngToPx(d); });

var nodes = d3.select("body").selectAll("svg").data(data).enter().append("svg");

var force = d3.layout.force().nodes(data);
force.on("tick", function() {
 nodes.style('left', function(d){ return (d.x - lm.config.offset) + 'px';})
 .style('top', function(d){ return (d.y - lm.config.offset) + 'px';});
});

licensed under cc by-sa 3.0 with attribution.