2013-11-19 2 views
2

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

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

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

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

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';}); 
});